pax_global_header 0000666 0000000 0000000 00000000064 12666203034 0014514 g ustar 00root root 0000000 0000000 52 comment=baaa78051cb4def07c607e606cb78a19508f671b
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/ 0000775 0000000 0000000 00000000000 12666203034 0021177 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.cproject 0000664 0000000 0000000 00000337203 12666203034 0023021 0 ustar 00root root 0000000 0000000
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.externalToolBuilders/ 0000775 0000000 0000000 00000000000 12666203034 0025427 5 ustar 00root root 0000000 0000000 bitbake compile -f [Builder].launch 0000664 0000000 0000000 00000001372 12666203034 0033703 0 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.externalToolBuilders
bitbake compile [Builder].launch 0000664 0000000 0000000 00000001552 12666203034 0033420 0 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.externalToolBuilders
bitbake deploy [Builder].launch 0000664 0000000 0000000 00000001762 12666203034 0033267 0 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.externalToolBuilders
kernel modules [Builder].launch 0000664 0000000 0000000 00000001257 12666203034 0033321 0 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.externalToolBuilders
org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder.launch 0000664 0000000 0000000 00000001361 12666203034 0041446 0 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.externalToolBuilders
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.gitignore 0000664 0000000 0000000 00000000045 12666203034 0023166 0 ustar 00root root 0000000 0000000 bin
tmp
Debug
Release
linux
sysroots
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.project 0000664 0000000 0000000 00000005211 12666203034 0022645 0 ustar 00root root 0000000 0000000
linux-elphel
org.eclipse.cdt.managedbuilder.core.genmakebuilder
full,incremental,
org.eclipse.ui.externaltools.ExternalToolBuilder
full,incremental,
LaunchConfigHandle
<project>/.externalToolBuilders/org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder.launch
org.eclipse.ui.externaltools.ExternalToolBuilder
clean,
LaunchConfigHandle
<project>/.externalToolBuilders/bitbake compile -f [Builder].launch
org.eclipse.ui.externaltools.ExternalToolBuilder
full,incremental,
LaunchConfigHandle
<project>/.externalToolBuilders/bitbake compile [Builder].launch
incclean
true
org.eclipse.ui.externaltools.ExternalToolBuilder
full,incremental,
LaunchConfigHandle
<project>/.externalToolBuilders/bitbake deploy [Builder].launch
incclean
true
org.eclipse.ui.externaltools.ExternalToolBuilder
full,incremental,
LaunchConfigHandle
<project>/.externalToolBuilders/kernel modules [Builder].launch
org.eclipse.cdt.core.cnature
org.eclipse.cdt.managedbuilder.core.managedBuildNature
org.eclipse.cdt.managedbuilder.core.ScannerConfigNature
1416246125600
26
org.eclipse.ui.ide.multiFilter
1.0-name-matches-false-false-src
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.pydevproject 0000664 0000000 0000000 00000000456 12666203034 0023723 0 ustar 00root root 0000000 0000000
Default
python 2.7
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.settings/ 0000775 0000000 0000000 00000000000 12666203034 0023115 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.settings/language.settings.xml 0000664 0000000 0000000 00000001117 12666203034 0027261 0 ustar 00root root 0000000 0000000
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.settings/org.eclipse.cdt.codan.core.prefs 0000664 0000000 0000000 00000024130 12666203034 0031153 0 ustar 00root root 0000000 0000000 eclipse.preferences.version=1
org.eclipse.cdt.codan.checkers.errnoreturn=Warning
org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false}
org.eclipse.cdt.codan.checkers.errreturnvalue=Error
org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.checkers.noreturn=Error
org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false}
org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error
org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error
org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning
org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error
org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning
org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false}
org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning
org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()}
org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error
org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning
org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true}
org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error
org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error
org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error
org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error
org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info
org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()}
org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning
org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error
org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error
org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error
org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning
org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>()}
org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning
org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},paramNot\=>false}
org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning
org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},else\=>false,afterelse\=>false}
org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning
org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true}
org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning
org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true}
org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning
org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")}
org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/.settings/org.eclipse.cdt.core.prefs 0000664 0000000 0000000 00000002342 12666203034 0030071 0 ustar 00root root 0000000 0000000 eclipse.preferences.version=1
environment/project/cdt.managedbuild.config.gnu.cross.exe.debug.903609687/PATH/delimiter=\:
environment/project/cdt.managedbuild.config.gnu.cross.exe.debug.903609687/PATH/operation=replace
environment/project/cdt.managedbuild.config.gnu.cross.exe.debug.903609687/PATH/value=${ProjDirPath}/sysroots/x86_64-linux/usr/bin/armv7a-vfp-neon-poky-linux-gnueabi/\:/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/usr/games\:/usr/local/games\:/usr/lib/jvm/java-8-oracle/bin\:/usr/lib/jvm/java-8-oracle/db/bin\:/usr/lib/jvm/java-8-oracle/jre/bin
environment/project/cdt.managedbuild.config.gnu.cross.exe.debug.903609687/append=true
environment/project/cdt.managedbuild.config.gnu.cross.exe.debug.903609687/appendContributed=true
indexer/indexAllFiles=false
indexer/indexAllHeaderVersions=true
indexer/indexAllVersionsSpecificHeaders=
indexer/indexOnOpen=true
indexer/indexUnusedHeadersWithDefaultLang=false
indexer/indexerId=org.eclipse.cdt.core.fastIndexer
indexer/skipFilesLargerThanMB=999
indexer/skipImplicitReferences=false
indexer/skipIncludedFilesLargerThanMB=999
indexer/skipMacroReferences=false
indexer/skipReferences=false
indexer/skipTypeReferences=false
indexer/useHeuristicIncludeResolution=false
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/README.md 0000664 0000000 0000000 00000003622 12666203034 0022461 0 ustar 00root root 0000000 0000000 # linux-elphel
Extras (drivers, etc.) and patches for the kernel
##Downloading
Code must be located in `poky/` directory. Navigate to `poky/` and run:
```
git clone https://github.com/Elphel/linux-elphel.git
```
##Generating links and headers
Links between project tree and kernel source tree are generated by Bitbake during 'link' task when using `meta-elphel393`. Some required header files are automatically generated during the build process.
```
. ./oe-init-build-env
bitbake linux-xlnx -c clean -f
bitbake linux-xlnx -c link -f
bitbake linux-xlnx -f
```
##Importing project into Eclipse
Run Eclipse from its location directory and provide additional heap memory to it.
```
./eclipse -vmargs -Xmx4G
```
- File → Import... → General → Existing Project into Workspace
- [Next] → Select root directory → Browse → specify project location (`poky/linux-elphel/`) → [OK] → [Finish]
Project now is imported into Eclipse workspace.
- Project → Properties
- C/C++ General → Preprocessor Include Paths → Entries → GNU C → CDT User Settings
- [Add...] → Select "Preprocessor macros file" → `linux/include/generated/autoconf.h` → [OK]
- [Add...] → Select "Preprocessor macros file" → `linux/include/linux/compiler.h` → [OK]
- [Add...] → Select "Include file" → `linux/include/linux/kconfig.h` → [OK]
- C/C++ General → Indexer
- Check “Enable project specific setttings”
- Check “Enable indexer”
- Uncheck “Index source files not included in the build”
- Uncheck “Index unused headers”
- Check “Index header variants”
- Uncheck “Index source and header files opened in editor”
- Uncheck “Allow heuristic resolution of includes”
- Set size of files to be skipped >100MB (effectively disabling this feature)
- Uncheck all “Skip…” options
- [OK] to close the Advanced Settings window.
- Project → C/C++ Index → Rebuild
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/run_bitbake.sh 0000775 0000000 0000000 00000000330 12666203034 0024017 0 ustar 00root root 0000000 0000000 #!/bin/bash
args="$@"
while (( "$#" )); do
shift
done
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo "Launching bitbake $args"
cd $DIR/..
. ./oe-init-build-env
bitbake $args | sed -u 's@| @@'
exit 0
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/ 0000775 0000000 0000000 00000000000 12666203034 0021766 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/ 0000775 0000000 0000000 00000000000 12666203034 0023444 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/ata/ 0000775 0000000 0000000 00000000000 12666203034 0024211 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/ata/Kconfig 0000664 0000000 0000000 00000062003 12666203034 0025515 0 ustar 00root root 0000000 0000000 #
# SATA/PATA driver configuration
#
config HAVE_PATA_PLATFORM
bool
help
This is an internal configuration node for any machine that
uses pata-platform driver to enable the relevant driver in the
configuration structure without having to submit endless patches
to update the PATA_PLATFORM entry.
menuconfig ATA
tristate "Serial ATA and Parallel ATA drivers (libata)"
depends on HAS_IOMEM
depends on BLOCK
depends on !(M32R || M68K || S390) || BROKEN
select SCSI
select GLOB
---help---
If you want to use an ATA hard disk, ATA tape drive, ATA CD-ROM or
any other ATA device under Linux, say Y and make sure that you know
the name of your ATA host adapter (the card inside your computer
that "speaks" the ATA protocol, also called ATA controller),
because you will be asked for it.
NOTE: ATA enables basic SCSI support; *however*,
'SCSI disk support', 'SCSI tape support', or
'SCSI CDROM support' may also be needed,
depending on your hardware configuration.
if ATA
config ATA_NONSTANDARD
bool
default n
config ATA_VERBOSE_ERROR
bool "Verbose ATA error reporting"
default y
help
This option adds parsing of ATA command descriptions and error bits
in libata kernel output, making it easier to interpret.
This option will enlarge the kernel by approx. 6KB. Disable it only
if kernel size is more important than ease of debugging.
If unsure, say Y.
config ATA_ACPI
bool "ATA ACPI Support"
depends on ACPI && PCI
default y
help
This option adds support for ATA-related ACPI objects.
These ACPI objects add the ability to retrieve taskfiles
from the ACPI BIOS and write them to the disk controller.
These objects may be related to performance, security,
power management, or other areas.
You can disable this at kernel boot time by using the
option libata.noacpi=1
config SATA_ZPODD
bool "SATA Zero Power Optical Disc Drive (ZPODD) support"
depends on ATA_ACPI && PM
default n
help
This option adds support for SATA Zero Power Optical Disc
Drive (ZPODD). It requires both the ODD and the platform
support, and if enabled, will automatically power on/off the
ODD when certain condition is satisfied. This does not impact
end user's experience of the ODD, only power is saved when
the ODD is not in use (i.e. no disc inside).
If unsure, say N.
config SATA_PMP
bool "SATA Port Multiplier support"
default y
help
This option adds support for SATA Port Multipliers
(the SATA version of an ethernet hub, or SAS expander).
comment "Controllers with non-SFF native interface"
config SATA_AHCI
tristate "AHCI SATA support"
depends on PCI
help
This option enables support for AHCI Serial ATA.
If unsure, say N.
config SATA_AHCI_PLATFORM
tristate "Platform AHCI SATA support"
help
This option enables support for Platform AHCI Serial ATA
controllers.
If unsure, say N.
config AHCI_DA850
tristate "DaVinci DA850 AHCI SATA support"
depends on ARCH_DAVINCI_DA850
help
This option enables support for the DaVinci DA850 SoC's
onboard AHCI SATA.
If unsure, say N.
config AHCI_ST
tristate "ST AHCI SATA support"
depends on ARCH_STI
help
This option enables support for ST AHCI SATA controller.
If unsure, say N.
config AHCI_IMX
tristate "Freescale i.MX AHCI SATA support"
depends on MFD_SYSCON && (ARCH_MXC || COMPILE_TEST)
help
This option enables support for the Freescale i.MX SoC's
onboard AHCI SATA.
If unsure, say N.
config AHCI_CEVA
tristate "Ceva AHCI SATA support"
depends on OF
help
This option enables support for the Xilinx Zynq
UltraScale+ MPSoC's onboard Ceva AHCI SATA.
If unsure, say N.
config AHCI_MVEBU
tristate "Marvell EBU AHCI SATA support"
depends on ARCH_MVEBU
help
This option enables support for the Marvebu EBU SoC's
onboard AHCI SATA.
If unsure, say N.
config AHCI_SUNXI
tristate "Allwinner sunxi AHCI SATA support"
depends on ARCH_SUNXI
help
This option enables support for the Allwinner sunxi SoC's
onboard AHCI SATA.
If unsure, say N.
config AHCI_TEGRA
tristate "NVIDIA Tegra124 AHCI SATA support"
depends on ARCH_TEGRA
help
This option enables support for the NVIDIA Tegra124 SoC's
onboard AHCI SATA.
If unsure, say N.
config AHCI_XGENE
tristate "APM X-Gene 6.0Gbps AHCI SATA host controller support"
depends on PHY_XGENE
help
This option enables support for APM X-Gene SoC SATA host controller.
config AHCI_ELPHEL
tristate "Elphel AHCI SATA driver support for elphel393 camera series"
depends on ARM
default m if ARM
help
This option enables support for Elphel AHCI SATA controller in elphel393
series cameras.
If unsure, say N.
config SATA_FSL
tristate "Freescale 3.0Gbps SATA support"
depends on FSL_SOC
help
This option enables support for Freescale 3.0Gbps SATA controller.
It can be found on MPC837x and MPC8315.
If unsure, say N.
config SATA_INIC162X
tristate "Initio 162x SATA support (Very Experimental)"
depends on PCI
help
This option enables support for Initio 162x Serial ATA.
config SATA_ACARD_AHCI
tristate "ACard AHCI variant (ATP 8620)"
depends on PCI
help
This option enables support for Acard.
If unsure, say N.
config SATA_SIL24
tristate "Silicon Image 3124/3132 SATA support"
depends on PCI
help
This option enables support for Silicon Image 3124/3132 Serial ATA.
If unsure, say N.
config ATA_SFF
bool "ATA SFF support (for legacy IDE and PATA)"
default y
help
This option adds support for ATA controllers with SFF
compliant or similar programming interface.
SFF is the legacy IDE interface that has been around since
the dawn of time. Almost all PATA controllers have an
SFF interface. Many SATA controllers have an SFF interface
when configured into a legacy compatibility mode.
For users with exclusively modern controllers like AHCI,
Silicon Image 3124, or Marvell 6440, you may choose to
disable this unneeded SFF support.
If unsure, say Y.
if ATA_SFF
comment "SFF controllers with custom DMA interface"
config PDC_ADMA
tristate "Pacific Digital ADMA support"
depends on PCI
help
This option enables support for Pacific Digital ADMA controllers
If unsure, say N.
config PATA_OCTEON_CF
tristate "OCTEON Boot Bus Compact Flash support"
depends on CAVIUM_OCTEON_SOC
help
This option enables a polled compact flash driver for use with
compact flash cards attached to the OCTEON boot bus.
If unsure, say N.
config SATA_QSTOR
tristate "Pacific Digital SATA QStor support"
depends on PCI
help
This option enables support for Pacific Digital Serial ATA QStor.
If unsure, say N.
config SATA_SX4
tristate "Promise SATA SX4 support (Experimental)"
depends on PCI
help
This option enables support for Promise Serial ATA SX4.
If unsure, say N.
config ATA_BMDMA
bool "ATA BMDMA support"
default y
help
This option adds support for SFF ATA controllers with BMDMA
capability. BMDMA stands for bus-master DMA and is the
de facto DMA interface for SFF controllers.
If unsure, say Y.
if ATA_BMDMA
comment "SATA SFF controllers with BMDMA"
config ATA_PIIX
tristate "Intel ESB, ICH, PIIX3, PIIX4 PATA/SATA support"
depends on PCI
help
This option enables support for ICH5/6/7/8 Serial ATA
and support for PATA on the Intel ESB/ICH/PIIX3/PIIX4 series
host controllers.
If unsure, say N.
config SATA_DWC
tristate "DesignWare Cores SATA support"
depends on 460EX
help
This option enables support for the on-chip SATA controller of the
AppliedMicro processor 460EX.
If unsure, say N.
config SATA_DWC_DEBUG
bool "Debugging driver version"
depends on SATA_DWC
help
This option enables debugging output in the driver.
config SATA_DWC_VDEBUG
bool "Verbose debug output"
depends on SATA_DWC_DEBUG
help
This option enables the taskfile dumping and NCQ debugging.
config SATA_HIGHBANK
tristate "Calxeda Highbank SATA support"
depends on ARCH_HIGHBANK || COMPILE_TEST
help
This option enables support for the Calxeda Highbank SoC's
onboard SATA.
If unsure, say N.
config SATA_MV
tristate "Marvell SATA support"
depends on PCI || ARCH_DOVE || ARCH_MV78XX0 || \
ARCH_MVEBU || ARCH_ORION5X || COMPILE_TEST
select GENERIC_PHY
help
This option enables support for the Marvell Serial ATA family.
Currently supports 88SX[56]0[48][01] PCI(-X) chips,
as well as the newer [67]042 PCI-X/PCIe and SOC devices.
If unsure, say N.
config SATA_NV
tristate "NVIDIA SATA support"
depends on PCI
help
This option enables support for NVIDIA Serial ATA.
If unsure, say N.
config SATA_PROMISE
tristate "Promise SATA TX2/TX4 support"
depends on PCI
help
This option enables support for Promise Serial ATA TX2/TX4.
If unsure, say N.
config SATA_RCAR
tristate "Renesas R-Car SATA support"
depends on ARCH_SHMOBILE || COMPILE_TEST
help
This option enables support for Renesas R-Car Serial ATA.
If unsure, say N.
config SATA_SIL
tristate "Silicon Image SATA support"
depends on PCI
help
This option enables support for Silicon Image Serial ATA.
If unsure, say N.
config SATA_SIS
tristate "SiS 964/965/966/180 SATA support"
depends on PCI
select PATA_SIS
help
This option enables support for SiS Serial ATA on
SiS 964/965/966/180 and Parallel ATA on SiS 180.
The PATA support for SiS 180 requires additionally to
enable the PATA_SIS driver in the config.
If unsure, say N.
config SATA_SVW
tristate "ServerWorks Frodo / Apple K2 SATA support"
depends on PCI
help
This option enables support for Broadcom/Serverworks/Apple K2
SATA support.
If unsure, say N.
config SATA_ULI
tristate "ULi Electronics SATA support"
depends on PCI
help
This option enables support for ULi Electronics SATA.
If unsure, say N.
config SATA_VIA
tristate "VIA SATA support"
depends on PCI
help
This option enables support for VIA Serial ATA.
If unsure, say N.
config SATA_VITESSE
tristate "VITESSE VSC-7174 / INTEL 31244 SATA support"
depends on PCI
help
This option enables support for Vitesse VSC7174 and Intel 31244 Serial ATA.
If unsure, say N.
comment "PATA SFF controllers with BMDMA"
config PATA_ALI
tristate "ALi PATA support"
depends on PCI
help
This option enables support for the ALi ATA interfaces
found on the many ALi chipsets.
If unsure, say N.
config PATA_AMD
tristate "AMD/NVidia PATA support"
depends on PCI
help
This option enables support for the AMD and NVidia PATA
interfaces found on the chipsets for Athlon/Athlon64.
If unsure, say N.
config PATA_ARASAN_CF
tristate "ARASAN CompactFlash PATA Controller Support"
depends on ARCH_SPEAR13XX || COMPILE_TEST
depends on DMADEVICES
select DMA_ENGINE
help
Say Y here to support the ARASAN CompactFlash PATA controller
config PATA_ARTOP
tristate "ARTOP 6210/6260 PATA support"
depends on PCI
help
This option enables support for ARTOP PATA controllers.
If unsure, say N.
config PATA_ATIIXP
tristate "ATI PATA support"
depends on PCI
help
This option enables support for the ATI ATA interfaces
found on the many ATI chipsets.
If unsure, say N.
config PATA_ATP867X
tristate "ARTOP/Acard ATP867X PATA support"
depends on PCI
help
This option enables support for ARTOP/Acard ATP867X PATA
controllers.
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_CMD64X
tristate "CMD64x PATA support"
depends on PCI
help
This option enables support for the CMD64x series chips
except for the CMD640.
If unsure, say N.
config PATA_CS5520
tristate "CS5510/5520 PATA support"
depends on PCI && (X86_32 || COMPILE_TEST)
help
This option enables support for the Cyrix 5510/5520
companion chip used with the MediaGX/Geode processor family.
If unsure, say N.
config PATA_CS5530
tristate "CS5530 PATA support"
depends on PCI && (X86_32 || COMPILE_TEST)
help
This option enables support for the Cyrix/NatSemi/AMD CS5530
companion chip used with the MediaGX/Geode processor family.
If unsure, say N.
config PATA_CS5535
tristate "CS5535 PATA support (Experimental)"
depends on PCI && X86_32
help
This option enables support for the NatSemi/AMD CS5535
companion chip used with the Geode processor family.
If unsure, say N.
config PATA_CS5536
tristate "CS5536 PATA support"
depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
help
This option enables support for the AMD CS5536
companion chip used with the Geode LX processor family.
If unsure, say N.
config PATA_CYPRESS
tristate "Cypress CY82C693 PATA support (Very Experimental)"
depends on PCI
help
This option enables support for the Cypress/Contaq CY82C693
chipset found in some Alpha systems
If unsure, say N.
config PATA_EFAR
tristate "EFAR SLC90E66 support"
depends on PCI
help
This option enables support for the EFAR SLC90E66
IDE controller found on some older machines.
If unsure, say N.
config PATA_EP93XX
tristate "Cirrus Logic EP93xx PATA support"
depends on ARCH_EP93XX
help
This option enables support for the PATA controller in
the Cirrus Logic EP9312 and EP9315 ARM CPU.
If unsure, say N.
config PATA_HPT366
tristate "HPT 366/368 PATA support"
depends on PCI
help
This option enables support for the HPT 366 and 368
PATA controllers via the new ATA layer.
If unsure, say N.
config PATA_HPT37X
tristate "HPT 370/370A/371/372/374/302 PATA support"
depends on PCI
help
This option enables support for the majority of the later HPT
PATA controllers via the new ATA layer.
If unsure, say N.
config PATA_HPT3X2N
tristate "HPT 371N/372N/302N PATA support"
depends on PCI
help
This option enables support for the N variant HPT PATA
controllers via the new ATA layer.
If unsure, say N.
config PATA_HPT3X3
tristate "HPT 343/363 PATA support"
depends on PCI
help
This option enables support for the HPT 343/363
PATA controllers via the new ATA layer
If unsure, say N.
config PATA_HPT3X3_DMA
bool "HPT 343/363 DMA support"
depends on PATA_HPT3X3
help
This option enables DMA support for the HPT343/363
controllers. Enable with care as there are still some
problems with DMA on this chipset.
config PATA_ICSIDE
tristate "Acorn ICS PATA support"
depends on ARM && ARCH_ACORN
help
On Acorn systems, say Y here if you wish to use the ICS PATA
interface card. This is not required for ICS partition support.
If you are unsure, say N to this.
config PATA_IMX
tristate "PATA support for Freescale iMX"
depends on ARCH_MXC
help
This option enables support for the PATA host available on Freescale
iMX SoCs.
If unsure, say N.
config PATA_IT8213
tristate "IT8213 PATA support (Experimental)"
depends on PCI
help
This option enables support for the ITE 821 PATA
controllers via the new ATA layer.
If unsure, say N.
config PATA_IT821X
tristate "IT8211/2 PATA support"
depends on PCI
help
This option enables support for the ITE 8211 and 8212
PATA controllers via the new ATA layer, including RAID
mode.
If unsure, say N.
config PATA_JMICRON
tristate "JMicron PATA support"
depends on PCI
help
Enable support for the JMicron IDE controller, via the new
ATA layer.
If unsure, say N.
config PATA_MACIO
tristate "Apple PowerMac/PowerBook internal 'MacIO' IDE"
depends on PPC_PMAC
help
Most IDE capable PowerMacs have IDE busses driven by a variant
of this controller which is part of the Apple chipset used on
most PowerMac models. Some models have multiple busses using
different chipsets, though generally, MacIO is one of them.
config PATA_MARVELL
tristate "Marvell PATA support via legacy mode"
depends on PCI
help
This option enables limited support for the Marvell 88SE61xx ATA
controllers. If you wish to use only the SATA ports then select
the AHCI driver alone. If you wish to the use the PATA port or
both SATA and PATA include this driver.
If unsure, say N.
config PATA_MPC52xx
tristate "Freescale MPC52xx SoC internal IDE"
depends on PPC_MPC52xx && PPC_BESTCOMM
select PPC_BESTCOMM_ATA
help
This option enables support for integrated IDE controller
of the Freescale MPC52xx SoC.
If unsure, say N.
config PATA_NETCELL
tristate "NETCELL Revolution RAID support"
depends on PCI
help
This option enables support for the Netcell Revolution RAID
PATA controller.
If unsure, say N.
config PATA_NINJA32
tristate "Ninja32/Delkin Cardbus ATA support"
depends on PCI
help
This option enables support for the Ninja32, Delkin and
possibly other brands of Cardbus ATA adapter
If unsure, say N.
config PATA_NS87415
tristate "Nat Semi NS87415 PATA support"
depends on PCI
help
This option enables support for the National Semiconductor
NS87415 PCI-IDE controller.
If unsure, say N.
config PATA_OLDPIIX
tristate "Intel PATA old PIIX support"
depends on PCI
help
This option enables support for early PIIX PATA support.
If unsure, say N.
config PATA_OPTIDMA
tristate "OPTI FireStar PATA support (Very Experimental)"
depends on PCI
help
This option enables DMA/PIO support for the later OPTi
controllers found on some old motherboards and in some
laptops.
If unsure, say N.
config PATA_PDC2027X
tristate "Promise PATA 2027x support"
depends on PCI
help
This option enables support for Promise PATA pdc20268 to pdc20277 host adapters.
If unsure, say N.
config PATA_PDC_OLD
tristate "Older Promise PATA controller support"
depends on PCI
help
This option enables support for the Promise 20246, 20262, 20263,
20265 and 20267 adapters.
If unsure, say N.
config PATA_RADISYS
tristate "RADISYS 82600 PATA support (Experimental)"
depends on PCI
help
This option enables support for the RADISYS 82600
PATA controllers via the new ATA layer
If unsure, say N.
config PATA_RDC
tristate "RDC PATA support"
depends on PCI
help
This option enables basic support for the later RDC PATA controllers
controllers via the new ATA layer. For the RDC 1010, you need to
enable the IT821X driver instead.
If unsure, say N.
config PATA_SC1200
tristate "SC1200 PATA support"
depends on PCI && (X86_32 || COMPILE_TEST)
help
This option enables support for the NatSemi/AMD SC1200 SoC
companion chip used with the Geode processor family.
If unsure, say N.
config PATA_SCC
tristate "Toshiba's Cell Reference Set IDE support"
depends on PCI && PPC_CELLEB
help
This option enables support for the built-in IDE controller on
Toshiba Cell Reference Board.
If unsure, say N.
config PATA_SCH
tristate "Intel SCH PATA support"
depends on PCI
help
This option enables support for Intel SCH PATA on the Intel
SCH (US15W, US15L, UL11L) series host controllers.
If unsure, say N.
config PATA_SERVERWORKS
tristate "SERVERWORKS OSB4/CSB5/CSB6/HT1000 PATA support"
depends on PCI
help
This option enables support for the Serverworks OSB4/CSB5/CSB6 and
HT1000 PATA controllers, via the new ATA layer.
If unsure, say N.
config PATA_SIL680
tristate "CMD / Silicon Image 680 PATA support"
depends on PCI
help
This option enables support for CMD / Silicon Image 680 PATA.
If unsure, say N.
config PATA_SIS
tristate "SiS PATA support"
depends on PCI
help
This option enables support for SiS PATA controllers
If unsure, say N.
config PATA_TOSHIBA
tristate "Toshiba Piccolo support (Experimental)"
depends on PCI
help
Support for the Toshiba Piccolo controllers. Currently only the
primary channel is supported by this driver.
If unsure, say N.
config PATA_TRIFLEX
tristate "Compaq Triflex PATA support"
depends on PCI
help
Enable support for the Compaq 'Triflex' IDE controller as found
on many Compaq Pentium-Pro systems, via the new ATA layer.
If unsure, say N.
config PATA_VIA
tristate "VIA PATA support"
depends on PCI
help
This option enables support for the VIA PATA interfaces
found on the many VIA chipsets.
If unsure, say N.
config PATA_PXA
tristate "PXA DMA-capable PATA support"
depends on ARCH_PXA
help
This option enables support for harddrive attached to PXA CPU's bus.
NOTE: This driver utilizes PXA DMA controller, in case your hardware
is not capable of doing MWDMA, use pata_platform instead.
If unsure, say N.
config PATA_WINBOND
tristate "Winbond SL82C105 PATA support"
depends on PCI
help
This option enables support for SL82C105 PATA devices found in the
Netwinder and some other systems
If unsure, say N.
endif # ATA_BMDMA
comment "PIO-only SFF controllers"
config PATA_AT32
tristate "Atmel AVR32 PATA support (Experimental)"
depends on AVR32 && PLATFORM_AT32AP
help
This option enables support for the IDE devices on the
Atmel AT32AP platform.
If unsure, say N.
config PATA_AT91
tristate "PATA support for AT91SAM9260"
depends on ARM && SOC_AT91SAM9
depends on !ARCH_MULTIPLATFORM
help
This option enables support for IDE devices on the Atmel AT91SAM9260 SoC.
If unsure, say N.
config PATA_CMD640_PCI
tristate "CMD640 PCI PATA support (Experimental)"
depends on PCI
help
This option enables support for the CMD640 PCI IDE
interface chip. Only the primary channel is currently
supported.
If unsure, say N.
config PATA_ISAPNP
tristate "ISA Plug and Play PATA support"
depends on ISAPNP
help
This option enables support for ISA plug & play ATA
controllers such as those found on old soundcards.
If unsure, say N.
config PATA_IXP4XX_CF
tristate "IXP4XX Compact Flash support"
depends on ARCH_IXP4XX
help
This option enables support for a Compact Flash connected on
the ixp4xx expansion bus. This driver had been written for
Loft/Avila boards in mind but can work with others.
If unsure, say N.
config PATA_MPIIX
tristate "Intel PATA MPIIX support"
depends on PCI
help
This option enables support for MPIIX PATA support.
If unsure, say N.
config PATA_NS87410
tristate "Nat Semi NS87410 PATA support"
depends on PCI
help
This option enables support for the National Semiconductor
NS87410 PCI-IDE controller.
If unsure, say N.
config PATA_OPTI
tristate "OPTI621/6215 PATA support (Very Experimental)"
depends on PCI
help
This option enables full PIO support for the early Opti ATA
controllers found on some old motherboards.
If unsure, say N.
config PATA_PALMLD
tristate "Palm LifeDrive PATA support"
depends on MACH_PALMLD
help
This option enables support for Palm LifeDrive's internal ATA
port via the new ATA layer.
If unsure, say N.
config PATA_PCMCIA
tristate "PCMCIA PATA support"
depends on PCMCIA
help
This option enables support for PCMCIA ATA interfaces, including
compact flash card adapters via the new ATA layer.
If unsure, say N.
config PATA_PLATFORM
tristate "Generic platform device PATA support"
depends on EXPERT || PPC || HAVE_PATA_PLATFORM
help
This option enables support for generic directly connected ATA
devices commonly found on embedded systems.
If unsure, say N.
config PATA_OF_PLATFORM
tristate "OpenFirmware platform device PATA support"
depends on PATA_PLATFORM && OF
help
This option enables support for generic directly connected ATA
devices commonly found on embedded systems with OpenFirmware
bindings.
If unsure, say N.
config PATA_QDI
tristate "QDI VLB PATA support"
depends on ISA
select PATA_LEGACY
help
Support for QDI 6500 and 6580 PATA controllers on VESA local bus.
config PATA_RB532
tristate "RouterBoard 532 PATA CompactFlash support"
depends on MIKROTIK_RB532
help
This option enables support for the RouterBoard 532
PATA CompactFlash controller.
If unsure, say N.
config PATA_RZ1000
tristate "PC Tech RZ1000 PATA support"
depends on PCI
help
This option enables basic support for the PC Tech RZ1000/1
PATA controllers via the new ATA layer
If unsure, say N.
config PATA_SAMSUNG_CF
tristate "Samsung SoC PATA support"
depends on SAMSUNG_DEV_IDE
help
This option enables basic support for Samsung's S3C/S5P board
PATA controllers via the new ATA layer
If unsure, say N.
config PATA_WINBOND_VLB
tristate "Winbond W83759A VLB PATA support (Experimental)"
depends on ISA
select PATA_LEGACY
help
Support for the Winbond W83759A controller on Vesa Local Bus
systems.
comment "Generic fallback / legacy drivers"
config PATA_ACPI
tristate "ACPI firmware driver for PATA"
depends on ATA_ACPI && ATA_BMDMA
help
This option enables an ACPI method driver which drives
motherboard PATA controller interfaces through the ACPI
firmware in the BIOS. This driver can sometimes handle
otherwise unsupported hardware.
config ATA_GENERIC
tristate "Generic ATA support"
depends on PCI && ATA_BMDMA
help
This option enables support for generic BIOS configured
ATA controllers via the new ATA layer
If unsure, say N.
config PATA_LEGACY
tristate "Legacy ISA PATA support (Experimental)"
depends on (ISA || PCI)
help
This option enables support for ISA/VLB/PCI bus legacy PATA
ports and allows them to be accessed via the new ATA layer.
If unsure, say N.
endif # ATA_SFF
endif # ATA
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/ata/Makefile 0000664 0000000 0000000 00000011563 12666203034 0025657 0 ustar 00root root 0000000 0000000
obj-$(CONFIG_ATA) += libata.o
# non-SFF interface
obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o libahci_debug.o
obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o
obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o libahci_debug.o
obj-$(CONFIG_AHCI_ELPHEL) += ahci_elphel.o libahci.o libahci_platform.o libahci_debug.o
obj-$(CONFIG_SATA_FSL) += sata_fsl.o
obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o
obj-$(CONFIG_AHCI_CEVA) += ahci_ceva.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_DA850) += ahci_da850.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_ST) += ahci_st.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_TEGRA) += ahci_tegra.o libahci.o libahci_platform.o
obj-$(CONFIG_AHCI_XGENE) += ahci_xgene.o libahci.o libahci_platform.o
# SFF w/ custom DMA
obj-$(CONFIG_PDC_ADMA) += pdc_adma.o
obj-$(CONFIG_PATA_ARASAN_CF) += pata_arasan_cf.o
obj-$(CONFIG_PATA_OCTEON_CF) += pata_octeon_cf.o
obj-$(CONFIG_SATA_QSTOR) += sata_qstor.o
obj-$(CONFIG_SATA_SX4) += sata_sx4.o
# SFF SATA w/ BMDMA
obj-$(CONFIG_ATA_PIIX) += ata_piix.o
obj-$(CONFIG_SATA_MV) += sata_mv.o
obj-$(CONFIG_SATA_NV) += sata_nv.o
obj-$(CONFIG_SATA_PROMISE) += sata_promise.o
obj-$(CONFIG_SATA_RCAR) += sata_rcar.o
obj-$(CONFIG_SATA_SIL) += sata_sil.o
obj-$(CONFIG_SATA_SIS) += sata_sis.o
obj-$(CONFIG_SATA_SVW) += sata_svw.o
obj-$(CONFIG_SATA_ULI) += sata_uli.o
obj-$(CONFIG_SATA_VIA) += sata_via.o
obj-$(CONFIG_SATA_VITESSE) += sata_vsc.o
# SFF PATA w/ BMDMA
obj-$(CONFIG_PATA_ALI) += pata_ali.o
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_CMD64X) += pata_cmd64x.o
obj-$(CONFIG_PATA_CS5520) += pata_cs5520.o
obj-$(CONFIG_PATA_CS5530) += pata_cs5530.o
obj-$(CONFIG_PATA_CS5535) += pata_cs5535.o
obj-$(CONFIG_PATA_CS5536) += pata_cs5536.o
obj-$(CONFIG_PATA_CYPRESS) += pata_cypress.o
obj-$(CONFIG_PATA_EFAR) += pata_efar.o
obj-$(CONFIG_PATA_EP93XX) += pata_ep93xx.o
obj-$(CONFIG_PATA_HPT366) += pata_hpt366.o
obj-$(CONFIG_PATA_HPT37X) += pata_hpt37x.o
obj-$(CONFIG_PATA_HPT3X2N) += pata_hpt3x2n.o
obj-$(CONFIG_PATA_HPT3X3) += pata_hpt3x3.o
obj-$(CONFIG_PATA_ICSIDE) += pata_icside.o
obj-$(CONFIG_PATA_IMX) += pata_imx.o
obj-$(CONFIG_PATA_IT8213) += pata_it8213.o
obj-$(CONFIG_PATA_IT821X) += pata_it821x.o
obj-$(CONFIG_PATA_JMICRON) += pata_jmicron.o
obj-$(CONFIG_PATA_MACIO) += pata_macio.o
obj-$(CONFIG_PATA_MARVELL) += pata_marvell.o
obj-$(CONFIG_PATA_MPC52xx) += pata_mpc52xx.o
obj-$(CONFIG_PATA_NETCELL) += pata_netcell.o
obj-$(CONFIG_PATA_NINJA32) += pata_ninja32.o
obj-$(CONFIG_PATA_NS87415) += pata_ns87415.o
obj-$(CONFIG_PATA_OLDPIIX) += pata_oldpiix.o
obj-$(CONFIG_PATA_OPTIDMA) += pata_optidma.o
obj-$(CONFIG_PATA_PDC2027X) += pata_pdc2027x.o
obj-$(CONFIG_PATA_PDC_OLD) += pata_pdc202xx_old.o
obj-$(CONFIG_PATA_RADISYS) += pata_radisys.o
obj-$(CONFIG_PATA_RDC) += pata_rdc.o
obj-$(CONFIG_PATA_SC1200) += pata_sc1200.o
obj-$(CONFIG_PATA_SCC) += pata_scc.o
obj-$(CONFIG_PATA_SCH) += pata_sch.o
obj-$(CONFIG_PATA_SERVERWORKS) += pata_serverworks.o
obj-$(CONFIG_PATA_SIL680) += pata_sil680.o
obj-$(CONFIG_PATA_SIS) += pata_sis.o
obj-$(CONFIG_PATA_TOSHIBA) += pata_piccolo.o
obj-$(CONFIG_PATA_TRIFLEX) += pata_triflex.o
obj-$(CONFIG_PATA_VIA) += pata_via.o
obj-$(CONFIG_PATA_WINBOND) += pata_sl82c105.o
# SFF PIO only
obj-$(CONFIG_PATA_AT32) += pata_at32.o
obj-$(CONFIG_PATA_AT91) += pata_at91.o
obj-$(CONFIG_PATA_CMD640_PCI) += pata_cmd640.o
obj-$(CONFIG_PATA_ISAPNP) += pata_isapnp.o
obj-$(CONFIG_PATA_IXP4XX_CF) += pata_ixp4xx_cf.o
obj-$(CONFIG_PATA_MPIIX) += pata_mpiix.o
obj-$(CONFIG_PATA_NS87410) += pata_ns87410.o
obj-$(CONFIG_PATA_OPTI) += pata_opti.o
obj-$(CONFIG_PATA_PCMCIA) += pata_pcmcia.o
obj-$(CONFIG_PATA_PALMLD) += pata_palmld.o
obj-$(CONFIG_PATA_PLATFORM) += pata_platform.o
obj-$(CONFIG_PATA_OF_PLATFORM) += pata_of_platform.o
obj-$(CONFIG_PATA_RB532) += pata_rb532_cf.o
obj-$(CONFIG_PATA_RZ1000) += pata_rz1000.o
obj-$(CONFIG_PATA_SAMSUNG_CF) += pata_samsung_cf.o
obj-$(CONFIG_PATA_PXA) += pata_pxa.o
# Should be last but two libata driver
obj-$(CONFIG_PATA_ACPI) += pata_acpi.o
# Should be last but one libata driver
obj-$(CONFIG_ATA_GENERIC) += ata_generic.o
# Should be last libata driver
obj-$(CONFIG_PATA_LEGACY) += pata_legacy.o
libata-y := libata-core.o libata-scsi.o libata-eh.o libata-transport.o
libata-$(CONFIG_ATA_SFF) += libata-sff.o
libata-$(CONFIG_SATA_PMP) += libata-pmp.o
libata-$(CONFIG_ATA_ACPI) += libata-acpi.o
libata-$(CONFIG_SATA_ZPODD) += libata-zpodd.o
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/ata/ahci.c 0000664 0000000 0000000 00000140046 12666203034 0025266 0 ustar 00root root 0000000 0000000 /*
* ahci.c - AHCI SATA support
*
* Maintained by: Tejun Heo
* Please ALWAYS copy linux-ide@vger.kernel.org
* on emails.
*
* Copyright 2004-2005 Red Hat, 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 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, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* libata documentation is available via 'make {ps|pdf}docs',
* as Documentation/DocBook/libata.*
*
* AHCI hardware documentation:
* http://www.intel.com/technology/serialata/pdf/rev1_0.pdf
* http://www.intel.com/technology/serialata/pdf/rev1_1.pdf
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ahci.h"
#define DRV_NAME "ahci"
#define DRV_VERSION "3.0"
enum {
AHCI_PCI_BAR_STA2X11 = 0,
AHCI_PCI_BAR_ENMOTUS = 2,
AHCI_PCI_BAR_STANDARD = 5,
};
enum board_ids {
/* board IDs by feature in alphabetical order */
board_ahci,
board_ahci_ign_iferr,
board_ahci_nomsi,
board_ahci_noncq,
board_ahci_nosntf,
board_ahci_yes_fbs,
/* board IDs for specific chipsets in alphabetical order */
board_ahci_mcp65,
board_ahci_mcp77,
board_ahci_mcp89,
board_ahci_mv,
board_ahci_sb600,
board_ahci_sb700, /* for SB700 and SB800 */
board_ahci_vt8251,
/* aliases */
board_ahci_mcp_linux = board_ahci_mcp65,
board_ahci_mcp67 = board_ahci_mcp65,
board_ahci_mcp73 = board_ahci_mcp65,
board_ahci_mcp79 = board_ahci_mcp77,
};
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static void ahci_mcp89_apple_enable(struct pci_dev *pdev);
static bool is_mcp89_apple(struct pci_dev *pdev);
static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
#ifdef CONFIG_PM
static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
static int ahci_pci_device_resume(struct pci_dev *pdev);
#endif
static struct scsi_host_template ahci_sht = {
AHCI_SHT("ahci"),
};
static struct ata_port_operations ahci_vt8251_ops = {
.inherits = &ahci_ops,
.hardreset = ahci_vt8251_hardreset,
};
static struct ata_port_operations ahci_p5wdh_ops = {
.inherits = &ahci_ops,
.hardreset = ahci_p5wdh_hardreset,
};
static const struct ata_port_info ahci_port_info[] = {
/* by features */
[board_ahci] = {
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_ign_iferr] = {
AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_nomsi] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_MSI),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_noncq] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_nosntf] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_SNTF),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_yes_fbs] = {
AHCI_HFLAGS (AHCI_HFLAG_YES_FBS),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
/* by chipsets */
[board_ahci_mcp65] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP |
AHCI_HFLAG_YES_NCQ),
.flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_mcp77] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_mcp89] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_mv] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI |
AHCI_HFLAG_MV_PATA | AHCI_HFLAG_NO_PMP),
.flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[board_ahci_sb600] = {
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL |
AHCI_HFLAG_NO_MSI | AHCI_HFLAG_SECT255 |
AHCI_HFLAG_32BIT_ONLY),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_pmp_retry_srst_ops,
},
[board_ahci_sb700] = { /* for SB700 and SB800 */
AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_pmp_retry_srst_ops,
},
[board_ahci_vt8251] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_vt8251_ops,
},
};
static const struct pci_device_id ahci_pci_tbl[] = {
/* Intel */
{ PCI_VDEVICE(INTEL, 0x2652), board_ahci }, /* ICH6 */
{ PCI_VDEVICE(INTEL, 0x2653), board_ahci }, /* ICH6M */
{ PCI_VDEVICE(INTEL, 0x27c1), board_ahci }, /* ICH7 */
{ PCI_VDEVICE(INTEL, 0x27c5), board_ahci }, /* ICH7M */
{ PCI_VDEVICE(INTEL, 0x27c3), board_ahci }, /* ICH7R */
{ PCI_VDEVICE(AL, 0x5288), board_ahci_ign_iferr }, /* ULi M5288 */
{ PCI_VDEVICE(INTEL, 0x2681), board_ahci }, /* ESB2 */
{ PCI_VDEVICE(INTEL, 0x2682), board_ahci }, /* ESB2 */
{ PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */
{ PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */
{ PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */
{ PCI_VDEVICE(INTEL, 0x2822), board_ahci_nosntf }, /* ICH8 */
{ PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */
{ PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */
{ PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */
{ PCI_VDEVICE(INTEL, 0x2922), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x2923), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x2924), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x2925), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x2927), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x2929), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x292a), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x292b), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x292c), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x292f), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x294d), board_ahci }, /* ICH9 */
{ PCI_VDEVICE(INTEL, 0x294e), board_ahci }, /* ICH9M */
{ PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */
{ PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */
{ PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */
{ PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */
{ PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */
{ PCI_VDEVICE(INTEL, 0x3b22), board_ahci }, /* PCH AHCI */
{ PCI_VDEVICE(INTEL, 0x3b23), board_ahci }, /* PCH AHCI */
{ PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */
{ PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */
{ PCI_VDEVICE(INTEL, 0x3b29), board_ahci }, /* PCH AHCI */
{ PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */
{ PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */
{ PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */
{ PCI_VDEVICE(INTEL, 0x1c02), board_ahci }, /* CPT AHCI */
{ PCI_VDEVICE(INTEL, 0x1c03), board_ahci }, /* CPT AHCI */
{ PCI_VDEVICE(INTEL, 0x1c04), board_ahci }, /* CPT RAID */
{ PCI_VDEVICE(INTEL, 0x1c05), board_ahci }, /* CPT RAID */
{ PCI_VDEVICE(INTEL, 0x1c06), board_ahci }, /* CPT RAID */
{ PCI_VDEVICE(INTEL, 0x1c07), board_ahci }, /* CPT RAID */
{ PCI_VDEVICE(INTEL, 0x1d02), board_ahci }, /* PBG AHCI */
{ PCI_VDEVICE(INTEL, 0x1d04), board_ahci }, /* PBG RAID */
{ PCI_VDEVICE(INTEL, 0x1d06), board_ahci }, /* PBG RAID */
{ PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* PBG RAID */
{ PCI_VDEVICE(INTEL, 0x2323), board_ahci }, /* DH89xxCC AHCI */
{ PCI_VDEVICE(INTEL, 0x1e02), board_ahci }, /* Panther Point AHCI */
{ PCI_VDEVICE(INTEL, 0x1e03), board_ahci }, /* Panther Point AHCI */
{ PCI_VDEVICE(INTEL, 0x1e04), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e05), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e06), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e07), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e0e), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c02), board_ahci }, /* Lynx Point AHCI */
{ PCI_VDEVICE(INTEL, 0x8c03), board_ahci }, /* Lynx Point AHCI */
{ PCI_VDEVICE(INTEL, 0x8c04), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c05), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c06), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c07), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c0e), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c0f), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x9c02), board_ahci }, /* Lynx Point-LP AHCI */
{ PCI_VDEVICE(INTEL, 0x9c03), board_ahci }, /* Lynx Point-LP AHCI */
{ PCI_VDEVICE(INTEL, 0x9c04), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c05), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c06), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c07), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c0e), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c0f), board_ahci }, /* Lynx Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x1f22), board_ahci }, /* Avoton AHCI */
{ PCI_VDEVICE(INTEL, 0x1f23), board_ahci }, /* Avoton AHCI */
{ PCI_VDEVICE(INTEL, 0x1f24), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f25), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f26), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f32), board_ahci }, /* Avoton AHCI */
{ PCI_VDEVICE(INTEL, 0x1f33), board_ahci }, /* Avoton AHCI */
{ PCI_VDEVICE(INTEL, 0x1f34), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f35), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f36), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */
{ PCI_VDEVICE(INTEL, 0x8d04), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d06), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d0e), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d62), board_ahci }, /* Wellsburg AHCI */
{ PCI_VDEVICE(INTEL, 0x8d64), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d66), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d6e), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x23a3), board_ahci }, /* Coleto Creek AHCI */
{ PCI_VDEVICE(INTEL, 0x9c83), board_ahci }, /* Wildcat Point-LP AHCI */
{ PCI_VDEVICE(INTEL, 0x9c85), board_ahci }, /* Wildcat Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c87), board_ahci }, /* Wildcat Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9c8f), board_ahci }, /* Wildcat Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x8c82), board_ahci }, /* 9 Series AHCI */
{ PCI_VDEVICE(INTEL, 0x8c83), board_ahci }, /* 9 Series AHCI */
{ PCI_VDEVICE(INTEL, 0x8c84), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c85), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c86), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c87), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c8f), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x9d03), board_ahci }, /* Sunrise Point-LP AHCI */
{ PCI_VDEVICE(INTEL, 0x9d05), board_ahci }, /* Sunrise Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9d07), board_ahci }, /* Sunrise Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */
{ PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */
{ PCI_VDEVICE(INTEL, 0xa107), board_ahci }, /* Sunrise Point-H RAID */
{ PCI_VDEVICE(INTEL, 0xa10f), board_ahci }, /* Sunrise Point-H RAID */
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr },
/* JMicron 362B and 362C have an AHCI function with IDE class code */
{ PCI_VDEVICE(JMICRON, 0x2362), board_ahci_ign_iferr },
{ PCI_VDEVICE(JMICRON, 0x236f), board_ahci_ign_iferr },
/* ATI */
{ PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */
{ PCI_VDEVICE(ATI, 0x4390), board_ahci_sb700 }, /* ATI SB700/800 */
{ PCI_VDEVICE(ATI, 0x4391), board_ahci_sb700 }, /* ATI SB700/800 */
{ PCI_VDEVICE(ATI, 0x4392), board_ahci_sb700 }, /* ATI SB700/800 */
{ PCI_VDEVICE(ATI, 0x4393), board_ahci_sb700 }, /* ATI SB700/800 */
{ PCI_VDEVICE(ATI, 0x4394), board_ahci_sb700 }, /* ATI SB700/800 */
{ PCI_VDEVICE(ATI, 0x4395), board_ahci_sb700 }, /* ATI SB700/800 */
/* AMD */
{ PCI_VDEVICE(AMD, 0x7800), board_ahci }, /* AMD Hudson-2 */
{ PCI_VDEVICE(AMD, 0x7900), board_ahci }, /* AMD CZ */
/* AMD is using RAID class only for ahci controllers */
{ PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_RAID << 8, 0xffffff, board_ahci },
/* VIA */
{ PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */
{ PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */
/* NVIDIA */
{ PCI_VDEVICE(NVIDIA, 0x044c), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x044d), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x044e), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x044f), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x045c), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x045d), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x045e), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x045f), board_ahci_mcp65 }, /* MCP65 */
{ PCI_VDEVICE(NVIDIA, 0x0550), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0551), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0552), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0553), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0554), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0555), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0556), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0557), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0558), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0559), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x055a), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x055b), board_ahci_mcp67 }, /* MCP67 */
{ PCI_VDEVICE(NVIDIA, 0x0580), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0581), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0582), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0583), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0584), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0585), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0586), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0587), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0588), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x0589), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058a), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058b), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058c), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058d), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058e), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x058f), board_ahci_mcp_linux }, /* Linux ID */
{ PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci_mcp73 }, /* MCP73 */
{ PCI_VDEVICE(NVIDIA, 0x0ad0), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad1), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad2), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad3), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad4), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad5), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad6), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad7), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad8), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ad9), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ada), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0adb), board_ahci_mcp77 }, /* MCP77 */
{ PCI_VDEVICE(NVIDIA, 0x0ab4), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0ab5), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0ab6), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0ab7), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0ab8), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0ab9), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0aba), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0abb), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0abc), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0abd), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0abe), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0abf), board_ahci_mcp79 }, /* MCP79 */
{ PCI_VDEVICE(NVIDIA, 0x0d84), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d85), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d86), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d87), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d88), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d89), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8a), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8b), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8c), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8d), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8e), board_ahci_mcp89 }, /* MCP89 */
{ PCI_VDEVICE(NVIDIA, 0x0d8f), board_ahci_mcp89 }, /* MCP89 */
/* SiS */
{ PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */
{ PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 968 */
{ PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */
/* ST Microelectronics */
{ PCI_VDEVICE(STMICRO, 0xCC06), board_ahci }, /* ST ConneXt */
/* Marvell */
{ PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv }, /* 6145 */
{ PCI_VDEVICE(MARVELL, 0x6121), board_ahci_mv }, /* 6121 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9123),
.class = PCI_CLASS_STORAGE_SATA_AHCI,
.class_mask = 0xffffff,
.driver_data = board_ahci_yes_fbs }, /* 88se9128 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9125),
.driver_data = board_ahci_yes_fbs }, /* 88se9125 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_MARVELL_EXT, 0x9178,
PCI_VENDOR_ID_MARVELL_EXT, 0x9170),
.driver_data = board_ahci_yes_fbs }, /* 88se9170 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x917a),
.driver_data = board_ahci_yes_fbs }, /* 88se9172 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9172),
.driver_data = board_ahci_yes_fbs }, /* 88se9182 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9182),
.driver_data = board_ahci_yes_fbs }, /* 88se9172 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9192),
.driver_data = board_ahci_yes_fbs }, /* 88se9172 on some Gigabyte */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0),
.driver_data = board_ahci_yes_fbs },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a3),
.driver_data = board_ahci_yes_fbs },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9230),
.driver_data = board_ahci_yes_fbs },
{ PCI_DEVICE(PCI_VENDOR_ID_TTI, 0x0642),
.driver_data = board_ahci_yes_fbs },
/* Promise */
{ PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */
{ PCI_VDEVICE(PROMISE, 0x3781), board_ahci }, /* FastTrak TX8660 ahci-mode */
/* Asmedia */
{ PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci }, /* ASM1060 */
{ PCI_VDEVICE(ASMEDIA, 0x0602), board_ahci }, /* ASM1060 */
{ PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci }, /* ASM1061 */
{ PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */
/*
* Samsung SSDs found on some macbooks. NCQ times out if MSI is
* enabled. https://bugzilla.kernel.org/show_bug.cgi?id=60731
*/
{ PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi },
{ PCI_VDEVICE(SAMSUNG, 0xa800), board_ahci_nomsi },
/* Enmotus */
{ PCI_DEVICE(0x1c44, 0x8000), board_ahci },
/* Generic, PCI class code for AHCI */
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci },
{ } /* terminate list */
};
static struct pci_driver ahci_pci_driver = {
.name = DRV_NAME,
.id_table = ahci_pci_tbl,
.probe = ahci_init_one,
.remove = ata_pci_remove_one,
#ifdef CONFIG_PM
.suspend = ahci_pci_device_suspend,
.resume = ahci_pci_device_resume,
#endif
};
#if defined(CONFIG_PATA_MARVELL) || defined(CONFIG_PATA_MARVELL_MODULE)
static int marvell_enable;
#else
static int marvell_enable = 1;
#endif
module_param(marvell_enable, int, 0644);
MODULE_PARM_DESC(marvell_enable, "Marvell SATA via AHCI (1 = enabled)");
static void ahci_pci_save_initial_config(struct pci_dev *pdev,
struct ahci_host_priv *hpriv)
{
if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361) {
dev_info(&pdev->dev, "JMB361 has only one port\n");
hpriv->force_port_map = 1;
}
/*
* Temporary Marvell 6145 hack: PATA port presence
* is asserted through the standard AHCI port
* presence register, as bit 4 (counting from 0)
*/
if (hpriv->flags & AHCI_HFLAG_MV_PATA) {
if (pdev->device == 0x6121)
hpriv->mask_port_map = 0x3;
else
hpriv->mask_port_map = 0xf;
dev_info(&pdev->dev,
"Disabling your PATA port. Use the boot option 'ahci.marvell_enable=0' to avoid this.\n");
}
ahci_save_initial_config(&pdev->dev, hpriv);
}
static int ahci_pci_reset_controller(struct ata_host *host)
{
struct pci_dev *pdev = to_pci_dev(host->dev);
ahci_reset_controller(host);
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
struct ahci_host_priv *hpriv = host->private_data;
u16 tmp16;
/* configure PCS */
pci_read_config_word(pdev, 0x92, &tmp16);
if ((tmp16 & hpriv->port_map) != hpriv->port_map) {
tmp16 |= hpriv->port_map;
pci_write_config_word(pdev, 0x92, tmp16);
}
}
return 0;
}
static void ahci_pci_init_controller(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
struct pci_dev *pdev = to_pci_dev(host->dev);
void __iomem *port_mmio;
u32 tmp;
int mv;
if (hpriv->flags & AHCI_HFLAG_MV_PATA) {
if (pdev->device == 0x6121)
mv = 2;
else
mv = 4;
port_mmio = __ahci_port_base(host, mv);
writel(0, port_mmio + PORT_IRQ_MASK);
/* clear port IRQ */
tmp = readl(port_mmio + PORT_IRQ_STAT);
VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
if (tmp)
writel(tmp, port_mmio + PORT_IRQ_STAT);
}
ahci_init_controller(host);
}
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
struct ata_port *ap = link->ap;
struct ahci_host_priv *hpriv = ap->host->private_data;
bool online;
int rc;
DPRINTK("ENTER\n");
ahci_stop_engine(ap);
rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),
deadline, &online, NULL);
dev_info(ap->host->dev, "ahci_vt8251_hardreset()\n");
hpriv->start_engine(ap);
DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
/* vt8251 doesn't clear BSY on signature FIS reception,
* request follow-up softreset.
*/
return online ? -EAGAIN : rc;
}
static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
struct ata_taskfile tf;
bool online;
int rc;
dev_info(ap->host->dev, "ahci_p5wdh_hardreset()\n");
ahci_stop_engine(ap);
/* clear D2H reception area to properly wait for D2H FIS */
ata_tf_init(link->device, &tf);
tf.command = ATA_BUSY;
ata_tf_to_fis(&tf, 0, 0, d2h_fis);
rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),
deadline, &online, NULL);
hpriv->start_engine(ap);
/* The pseudo configuration device on SIMG4726 attached to
* ASUS P5W-DH Deluxe doesn't send signature FIS after
* hardreset if no device is attached to the first downstream
* port && the pseudo device locks up on SRST w/ PMP==0. To
* work around this, wait for !BSY only briefly. If BSY isn't
* cleared, perform CLO and proceed to IDENTIFY (achieved by
* ATA_LFLAG_NO_SRST and ATA_LFLAG_ASSUME_ATA).
*
* Wait for two seconds. Devices attached to downstream port
* which can't process the following IDENTIFY after this will
* have to be reset again. For most cases, this should
* suffice while making probing snappish enough.
*/
if (online) {
rc = ata_wait_after_reset(link, jiffies + 2 * HZ,
ahci_check_ready);
if (rc)
ahci_kick_engine(ap);
}
return rc;
}
#ifdef CONFIG_PM
static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
struct ata_host *host = pci_get_drvdata(pdev);
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
u32 ctl;
if (mesg.event & PM_EVENT_SUSPEND &&
hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
dev_err(&pdev->dev,
"BIOS update required for suspend/resume\n");
return -EIO;
}
if (mesg.event & PM_EVENT_SLEEP) {
/* AHCI spec rev1.1 section 8.3.3:
* Software must disable interrupts prior to requesting a
* transition of the HBA to D3 state.
*/
ctl = readl(mmio + HOST_CTL);
ctl &= ~HOST_IRQ_EN;
writel(ctl, mmio + HOST_CTL);
readl(mmio + HOST_CTL); /* flush */
}
return ata_pci_device_suspend(pdev, mesg);
}
static int ahci_pci_device_resume(struct pci_dev *pdev)
{
struct ata_host *host = pci_get_drvdata(pdev);
int rc;
rc = ata_pci_device_do_resume(pdev);
if (rc)
return rc;
/* Apple BIOS helpfully mangles the registers on resume */
if (is_mcp89_apple(pdev))
ahci_mcp89_apple_enable(pdev);
if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
rc = ahci_pci_reset_controller(host);
if (rc)
return rc;
ahci_pci_init_controller(host);
}
ata_host_resume(host);
return 0;
}
#endif
static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac)
{
int rc;
/*
* If the device fixup already set the dma_mask to some non-standard
* value, don't extend it here. This happens on STA2X11, for example.
*/
if (pdev->dma_mask && pdev->dma_mask < DMA_BIT_MASK(32))
return 0;
if (using_dac &&
!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
if (rc) {
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
dev_err(&pdev->dev,
"64-bit DMA enable failed\n");
return rc;
}
}
} else {
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
dev_err(&pdev->dev, "32-bit DMA enable failed\n");
return rc;
}
rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (rc) {
dev_err(&pdev->dev,
"32-bit consistent DMA enable failed\n");
return rc;
}
}
return 0;
}
static void ahci_pci_print_info(struct ata_host *host)
{
struct pci_dev *pdev = to_pci_dev(host->dev);
u16 cc;
const char *scc_s;
pci_read_config_word(pdev, 0x0a, &cc);
if (cc == PCI_CLASS_STORAGE_IDE)
scc_s = "IDE";
else if (cc == PCI_CLASS_STORAGE_SATA)
scc_s = "SATA";
else if (cc == PCI_CLASS_STORAGE_RAID)
scc_s = "RAID";
else
scc_s = "unknown";
ahci_print_info(host, scc_s);
}
/* On ASUS P5W DH Deluxe, the second port of PCI device 00:1f.2 is
* hardwired to on-board SIMG 4726. The chipset is ICH8 and doesn't
* support PMP and the 4726 either directly exports the device
* attached to the first downstream port or acts as a hardware storage
* controller and emulate a single ATA device (can be RAID 0/1 or some
* other configuration).
*
* When there's no device attached to the first downstream port of the
* 4726, "Config Disk" appears, which is a pseudo ATA device to
* configure the 4726. However, ATA emulation of the device is very
* lame. It doesn't send signature D2H Reg FIS after the initial
* hardreset, pukes on SRST w/ PMP==0 and has bunch of other issues.
*
* The following function works around the problem by always using
* hardreset on the port and not depending on receiving signature FIS
* afterward. If signature FIS isn't received soon, ATA class is
* assumed without follow-up softreset.
*/
static void ahci_p5wdh_workaround(struct ata_host *host)
{
static const struct dmi_system_id sysids[] = {
{
.ident = "P5W DH Deluxe",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"ASUSTEK COMPUTER INC"),
DMI_MATCH(DMI_PRODUCT_NAME, "P5W DH Deluxe"),
},
},
{ }
};
struct pci_dev *pdev = to_pci_dev(host->dev);
if (pdev->bus->number == 0 && pdev->devfn == PCI_DEVFN(0x1f, 2) &&
dmi_check_system(sysids)) {
struct ata_port *ap = host->ports[1];
dev_info(&pdev->dev,
"enabling ASUS P5W DH Deluxe on-board SIMG4726 workaround\n");
ap->ops = &ahci_p5wdh_ops;
ap->link.flags |= ATA_LFLAG_NO_SRST | ATA_LFLAG_ASSUME_ATA;
}
}
/*
* Macbook7,1 firmware forcibly disables MCP89 AHCI and changes PCI ID when
* booting in BIOS compatibility mode. We restore the registers but not ID.
*/
static void ahci_mcp89_apple_enable(struct pci_dev *pdev)
{
u32 val;
printk(KERN_INFO "ahci: enabling MCP89 AHCI mode\n");
pci_read_config_dword(pdev, 0xf8, &val);
val |= 1 << 0x1b;
/* the following changes the device ID, but appears not to affect function */
/* val = (val & ~0xf0000000) | 0x80000000; */
pci_write_config_dword(pdev, 0xf8, val);
pci_read_config_dword(pdev, 0x54c, &val);
val |= 1 << 0xc;
pci_write_config_dword(pdev, 0x54c, val);
pci_read_config_dword(pdev, 0x4a4, &val);
val &= 0xff;
val |= 0x01060100;
pci_write_config_dword(pdev, 0x4a4, val);
pci_read_config_dword(pdev, 0x54c, &val);
val &= ~(1 << 0xc);
pci_write_config_dword(pdev, 0x54c, val);
pci_read_config_dword(pdev, 0xf8, &val);
val &= ~(1 << 0x1b);
pci_write_config_dword(pdev, 0xf8, val);
}
static bool is_mcp89_apple(struct pci_dev *pdev)
{
return pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
pdev->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA &&
pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE &&
pdev->subsystem_device == 0xcb89;
}
/* only some SB600 ahci controllers can do 64bit DMA */
static bool ahci_sb600_enable_64bit(struct pci_dev *pdev)
{
static const struct dmi_system_id sysids[] = {
/*
* The oldest version known to be broken is 0901 and
* working is 1501 which was released on 2007-10-26.
* Enable 64bit DMA on 1501 and anything newer.
*
* Please read bko#9412 for more info.
*/
{
.ident = "ASUS M2A-VM",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"ASUSTeK Computer INC."),
DMI_MATCH(DMI_BOARD_NAME, "M2A-VM"),
},
.driver_data = "20071026", /* yyyymmdd */
},
/*
* All BIOS versions for the MSI K9A2 Platinum (MS-7376)
* support 64bit DMA.
*
* BIOS versions earlier than 1.5 had the Manufacturer DMI
* fields as "MICRO-STAR INTERANTIONAL CO.,LTD".
* This spelling mistake was fixed in BIOS version 1.5, so
* 1.5 and later have the Manufacturer as
* "MICRO-STAR INTERNATIONAL CO.,LTD".
* So try to match on DMI_BOARD_VENDOR of "MICRO-STAR INTER".
*
* BIOS versions earlier than 1.9 had a Board Product Name
* DMI field of "MS-7376". This was changed to be
* "K9A2 Platinum (MS-7376)" in version 1.9, but we can still
* match on DMI_BOARD_NAME of "MS-7376".
*/
{
.ident = "MSI K9A2 Platinum",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"MICRO-STAR INTER"),
DMI_MATCH(DMI_BOARD_NAME, "MS-7376"),
},
},
/*
* All BIOS versions for the MSI K9AGM2 (MS-7327) support
* 64bit DMA.
*
* This board also had the typo mentioned above in the
* Manufacturer DMI field (fixed in BIOS version 1.5), so
* match on DMI_BOARD_VENDOR of "MICRO-STAR INTER" again.
*/
{
.ident = "MSI K9AGM2",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"MICRO-STAR INTER"),
DMI_MATCH(DMI_BOARD_NAME, "MS-7327"),
},
},
/*
* All BIOS versions for the Asus M3A support 64bit DMA.
* (all release versions from 0301 to 1206 were tested)
*/
{
.ident = "ASUS M3A",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"ASUSTeK Computer INC."),
DMI_MATCH(DMI_BOARD_NAME, "M3A"),
},
},
{ }
};
const struct dmi_system_id *match;
int year, month, date;
char buf[9];
match = dmi_first_match(sysids);
if (pdev->bus->number != 0 || pdev->devfn != PCI_DEVFN(0x12, 0) ||
!match)
return false;
if (!match->driver_data)
goto enable_64bit;
dmi_get_date(DMI_BIOS_DATE, &year, &month, &date);
snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date);
if (strcmp(buf, match->driver_data) >= 0)
goto enable_64bit;
else {
dev_warn(&pdev->dev,
"%s: BIOS too old, forcing 32bit DMA, update BIOS\n",
match->ident);
return false;
}
enable_64bit:
dev_warn(&pdev->dev, "%s: enabling 64bit DMA\n", match->ident);
return true;
}
static bool ahci_broken_system_poweroff(struct pci_dev *pdev)
{
static const struct dmi_system_id broken_systems[] = {
{
.ident = "HP Compaq nx6310",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6310"),
},
/* PCI slot number of the controller */
.driver_data = (void *)0x1FUL,
},
{
.ident = "HP Compaq 6720s",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq 6720s"),
},
/* PCI slot number of the controller */
.driver_data = (void *)0x1FUL,
},
{ } /* terminate list */
};
const struct dmi_system_id *dmi = dmi_first_match(broken_systems);
if (dmi) {
unsigned long slot = (unsigned long)dmi->driver_data;
/* apply the quirk only to on-board controllers */
return slot == PCI_SLOT(pdev->devfn);
}
return false;
}
static bool ahci_broken_suspend(struct pci_dev *pdev)
{
static const struct dmi_system_id sysids[] = {
/*
* On HP dv[4-6] and HDX18 with earlier BIOSen, link
* to the harddisk doesn't become online after
* resuming from STR. Warn and fail suspend.
*
* http://bugzilla.kernel.org/show_bug.cgi?id=12276
*
* Use dates instead of versions to match as HP is
* apparently recycling both product and version
* strings.
*
* http://bugzilla.kernel.org/show_bug.cgi?id=15462
*/
{
.ident = "dv4",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME,
"HP Pavilion dv4 Notebook PC"),
},
.driver_data = "20090105", /* F.30 */
},
{
.ident = "dv5",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME,
"HP Pavilion dv5 Notebook PC"),
},
.driver_data = "20090506", /* F.16 */
},
{
.ident = "dv6",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME,
"HP Pavilion dv6 Notebook PC"),
},
.driver_data = "20090423", /* F.21 */
},
{
.ident = "HDX18",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME,
"HP HDX18 Notebook PC"),
},
.driver_data = "20090430", /* F.23 */
},
/*
* Acer eMachines G725 has the same problem. BIOS
* V1.03 is known to be broken. V3.04 is known to
* work. Between, there are V1.06, V2.06 and V3.03
* that we don't have much idea about. For now,
* blacklist anything older than V3.04.
*
* http://bugzilla.kernel.org/show_bug.cgi?id=15104
*/
{
.ident = "G725",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "eMachines"),
DMI_MATCH(DMI_PRODUCT_NAME, "eMachines G725"),
},
.driver_data = "20091216", /* V3.04 */
},
{ } /* terminate list */
};
const struct dmi_system_id *dmi = dmi_first_match(sysids);
int year, month, date;
char buf[9];
if (!dmi || pdev->bus->number || pdev->devfn != PCI_DEVFN(0x1f, 2))
return false;
dmi_get_date(DMI_BIOS_DATE, &year, &month, &date);
snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date);
return strcmp(buf, dmi->driver_data) < 0;
}
static bool ahci_broken_online(struct pci_dev *pdev)
{
#define ENCODE_BUSDEVFN(bus, slot, func) \
(void *)(unsigned long)(((bus) << 8) | PCI_DEVFN((slot), (func)))
static const struct dmi_system_id sysids[] = {
/*
* There are several gigabyte boards which use
* SIMG5723s configured as hardware RAID. Certain
* 5723 firmware revisions shipped there keep the link
* online but fail to answer properly to SRST or
* IDENTIFY when no device is attached downstream
* causing libata to retry quite a few times leading
* to excessive detection delay.
*
* As these firmwares respond to the second reset try
* with invalid device signature, considering unknown
* sig as offline works around the problem acceptably.
*/
{
.ident = "EP45-DQ6",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"Gigabyte Technology Co., Ltd."),
DMI_MATCH(DMI_BOARD_NAME, "EP45-DQ6"),
},
.driver_data = ENCODE_BUSDEVFN(0x0a, 0x00, 0),
},
{
.ident = "EP45-DS5",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR,
"Gigabyte Technology Co., Ltd."),
DMI_MATCH(DMI_BOARD_NAME, "EP45-DS5"),
},
.driver_data = ENCODE_BUSDEVFN(0x03, 0x00, 0),
},
{ } /* terminate list */
};
#undef ENCODE_BUSDEVFN
const struct dmi_system_id *dmi = dmi_first_match(sysids);
unsigned int val;
if (!dmi)
return false;
val = (unsigned long)dmi->driver_data;
return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff);
}
static bool ahci_broken_devslp(struct pci_dev *pdev)
{
/* device with broken DEVSLP but still showing SDS capability */
static const struct pci_device_id ids[] = {
{ PCI_VDEVICE(INTEL, 0x0f23)}, /* Valleyview SoC */
{}
};
return pci_match_id(ids, pdev);
}
#ifdef CONFIG_ATA_ACPI
static void ahci_gtf_filter_workaround(struct ata_host *host)
{
static const struct dmi_system_id sysids[] = {
/*
* Aspire 3810T issues a bunch of SATA enable commands
* via _GTF including an invalid one and one which is
* rejected by the device. Among the successful ones
* is FPDMA non-zero offset enable which when enabled
* only on the drive side leads to NCQ command
* failures. Filter it out.
*/
{
.ident = "Aspire 3810T",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3810T"),
},
.driver_data = (void *)ATA_ACPI_FILTER_FPDMA_OFFSET,
},
{ }
};
const struct dmi_system_id *dmi = dmi_first_match(sysids);
unsigned int filter;
int i;
if (!dmi)
return;
filter = (unsigned long)dmi->driver_data;
dev_info(host->dev, "applying extra ACPI _GTF filter 0x%x for %s\n",
filter, dmi->ident);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
struct ata_link *link;
struct ata_device *dev;
ata_for_each_link(link, ap, EDGE)
ata_for_each_dev(dev, link, ALL)
dev->gtf_filter |= filter;
}
}
#else
static inline void ahci_gtf_filter_workaround(struct ata_host *host)
{}
#endif
static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
struct ahci_host_priv *hpriv)
{
int rc, nvec;
if (hpriv->flags & AHCI_HFLAG_NO_MSI)
goto intx;
nvec = pci_msi_vec_count(pdev);
if (nvec < 0)
goto intx;
/*
* If number of MSIs is less than number of ports then Sharing Last
* Message mode could be enforced. In this case assume that advantage
* of multipe MSIs is negated and use single MSI mode instead.
*/
if (nvec < n_ports)
goto single_msi;
rc = pci_enable_msi_exact(pdev, nvec);
if (rc == -ENOSPC)
goto single_msi;
else if (rc < 0)
goto intx;
/* fallback to single MSI mode if the controller enforced MRSM mode */
if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) {
pci_disable_msi(pdev);
printk(KERN_INFO "ahci: MRSM is on, fallback to single MSI\n");
goto single_msi;
}
if (nvec > 1)
hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
return nvec;
single_msi:
if (pci_enable_msi(pdev))
goto intx;
return 1;
intx:
pci_intx(pdev, 1);
return 0;
}
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
unsigned int board_id = ent->driver_data;
struct ata_port_info pi = ahci_port_info[board_id];
const struct ata_port_info *ppi[] = { &pi, NULL };
struct device *dev = &pdev->dev;
struct ahci_host_priv *hpriv;
struct ata_host *host;
int n_ports, i, rc;
int ahci_pci_bar = AHCI_PCI_BAR_STANDARD;
VPRINTK("ENTER\n");
WARN_ON((int)ATA_MAX_QUEUE > AHCI_MAX_CMDS);
ata_print_version_once(&pdev->dev, DRV_VERSION);
/* The AHCI driver can only drive the SATA ports, the PATA driver
can drive them all so if both drivers are selected make sure
AHCI stays out of the way */
if (pdev->vendor == PCI_VENDOR_ID_MARVELL && !marvell_enable)
return -ENODEV;
/* Apple BIOS on MCP89 prevents us using AHCI */
if (is_mcp89_apple(pdev))
ahci_mcp89_apple_enable(pdev);
/* Promise's PDC42819 is a SAS/SATA controller that has an AHCI mode.
* At the moment, we can only use the AHCI mode. Let the users know
* that for SAS drives they're out of luck.
*/
if (pdev->vendor == PCI_VENDOR_ID_PROMISE)
dev_info(&pdev->dev,
"PDC42819 can only drive SATA devices with this driver\n");
/* Both Connext and Enmotus devices use non-standard BARs */
if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == 0xCC06)
ahci_pci_bar = AHCI_PCI_BAR_STA2X11;
else if (pdev->vendor == 0x1c44 && pdev->device == 0x8000)
ahci_pci_bar = AHCI_PCI_BAR_ENMOTUS;
/*
* The JMicron chip 361/363 contains one SATA controller and one
* PATA controller,for powering on these both controllers, we must
* follow the sequence one by one, otherwise one of them can not be
* powered on successfully, so here we disable the async suspend
* method for these chips.
*/
if (pdev->vendor == PCI_VENDOR_ID_JMICRON &&
(pdev->device == PCI_DEVICE_ID_JMICRON_JMB363 ||
pdev->device == PCI_DEVICE_ID_JMICRON_JMB361))
device_disable_async_suspend(&pdev->dev);
/* acquire resources */
rc = pcim_enable_device(pdev);
if (rc)
return rc;
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
(pdev->device == 0x2652 || pdev->device == 0x2653)) {
u8 map;
/* ICH6s share the same PCI ID for both piix and ahci
* modes. Enabling ahci mode while MAP indicates
* combined mode is a bad idea. Yield to ata_piix.
*/
pci_read_config_byte(pdev, ICH_MAP, &map);
if (map & 0x3) {
dev_info(&pdev->dev,
"controller is in combined mode, can't enable AHCI mode\n");
return -ENODEV;
}
}
/* AHCI controllers often implement SFF compatible interface.
* Grab all PCI BARs just in case.
*/
rc = pcim_iomap_regions_request_all(pdev, 1 << ahci_pci_bar, DRV_NAME);
if (rc == -EBUSY)
pcim_pin_device(pdev);
if (rc)
return rc;
hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
if (!hpriv)
return -ENOMEM;
hpriv->flags |= (unsigned long)pi.private_data;
/* MCP65 revision A1 and A2 can't do MSI */
if (board_id == board_ahci_mcp65 &&
(pdev->revision == 0xa1 || pdev->revision == 0xa2))
hpriv->flags |= AHCI_HFLAG_NO_MSI;
/* SB800 does NOT need the workaround to ignore SERR_INTERNAL */
if (board_id == board_ahci_sb700 && pdev->revision >= 0x40)
hpriv->flags &= ~AHCI_HFLAG_IGN_SERR_INTERNAL;
/* only some SB600s can do 64bit DMA */
if (ahci_sb600_enable_64bit(pdev))
hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY;
hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar];
/* must set flag prior to save config in order to take effect */
if (ahci_broken_devslp(pdev))
hpriv->flags |= AHCI_HFLAG_NO_DEVSLP;
/* save initial config */
ahci_pci_save_initial_config(pdev, hpriv);
/* prepare host */
if (hpriv->cap & HOST_CAP_NCQ) {
pi.flags |= ATA_FLAG_NCQ;
/*
* Auto-activate optimization is supposed to be
* supported on all AHCI controllers indicating NCQ
* capability, but it seems to be broken on some
* chipsets including NVIDIAs.
*/
if (!(hpriv->flags & AHCI_HFLAG_NO_FPDMA_AA))
pi.flags |= ATA_FLAG_FPDMA_AA;
/*
* All AHCI controllers should be forward-compatible
* with the new auxiliary field. This code should be
* conditionalized if any buggy AHCI controllers are
* encountered.
*/
pi.flags |= ATA_FLAG_FPDMA_AUX;
}
if (hpriv->cap & HOST_CAP_PMP)
pi.flags |= ATA_FLAG_PMP;
ahci_set_em_messages(hpriv, &pi);
if (ahci_broken_system_poweroff(pdev)) {
pi.flags |= ATA_FLAG_NO_POWEROFF_SPINDOWN;
dev_info(&pdev->dev,
"quirky BIOS, skipping spindown on poweroff\n");
}
if (ahci_broken_suspend(pdev)) {
hpriv->flags |= AHCI_HFLAG_NO_SUSPEND;
dev_warn(&pdev->dev,
"BIOS update required for suspend/resume\n");
}
if (ahci_broken_online(pdev)) {
hpriv->flags |= AHCI_HFLAG_SRST_TOUT_IS_OFFLINE;
dev_info(&pdev->dev,
"online status unreliable, applying workaround\n");
}
/* CAP.NP sometimes indicate the index of the last enabled
* port, at other times, that of the last possible port, so
* determining the maximum port number requires looking at
* both CAP.NP and port_map.
*/
n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
ahci_init_interrupts(pdev, n_ports, hpriv);
host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
if (!host)
return -ENOMEM;
host->private_data = hpriv;
if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
host->flags |= ATA_HOST_PARALLEL_SCAN;
else
dev_info(&pdev->dev, "SSS flag set, parallel bus scan disabled\n");
if (pi.flags & ATA_FLAG_EM)
ahci_reset_em(host);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
ata_port_pbar_desc(ap, ahci_pci_bar, -1, "abar");
ata_port_pbar_desc(ap, ahci_pci_bar,
0x100 + ap->port_no * 0x80, "port");
/* set enclosure management message type */
if (ap->flags & ATA_FLAG_EM)
ap->em_message_type = hpriv->em_msg_type;
/* disabled/not-implemented port */
if (!(hpriv->port_map & (1 << i)))
ap->ops = &ata_dummy_port_ops;
}
/* apply workaround for ASUS P5W DH Deluxe mainboard */
ahci_p5wdh_workaround(host);
/* apply gtf filter quirk */
ahci_gtf_filter_workaround(host);
/* initialize adapter */
rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64);
if (rc)
return rc;
rc = ahci_pci_reset_controller(host);
if (rc)
return rc;
ahci_pci_init_controller(host);
ahci_pci_print_info(host);
pci_set_master(pdev);
return ahci_host_activate(host, pdev->irq, &ahci_sht);
}
module_pci_driver(ahci_pci_driver);
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("AHCI SATA low-level driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, ahci_pci_tbl);
MODULE_VERSION(DRV_VERSION);
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/ata/ahci_elphel.c 0000664 0000000 0000000 00000023572 12666203034 0026623 0 ustar 00root root 0000000 0000000 /*
* Elphel AHCI SATA platform driver for elphel393 camera
*
* Based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov
*
* 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ahci.h"
#include "libahci_debug.h"
#define DRV_NAME "elphel-ahci"
/* Property names from device tree, these are specific for the controller */
#define PROP_NAME_CLB_OFFS "clb_offs"
#define PROP_NAME_FB_OFFS "fb_offs"
static struct ata_port_operations ahci_elphel_ops;
static const struct ata_port_info ahci_elphel_port_info;
static struct scsi_host_template ahci_platform_sht;
static const struct of_device_id ahci_elphel_of_match[];
struct elphel_ahci_priv {
u32 clb_offs;
u32 fb_offs;
u32 base_addr;
};
// What about port_stop and freeing/unmapping ?
// Or at least check if it is re-started and memory is already allocated/mapped
static int elphel_port_start(struct ata_port *ap)
{
void *mem;
dma_addr_t mem_dma;
struct device *dev = ap->host->dev;
struct ahci_port_priv *pp;
struct ahci_host_priv *hpriv = ap->host->private_data;
const struct elphel_ahci_priv *dpriv = hpriv->plat_data;
// const ssize_t align_cdt = 128;
const ssize_t align_cdt = 4096; // just trying - page align
u32 * dbg_p;
int dbg_i;
libahci_debug_init(ap->host);
dev_info(dev, "starting port %d", ap->port_no);
libahci_debug_wait_flag();
pp = devm_kzalloc(dev, sizeof(struct ahci_port_priv), GFP_KERNEL);
if (!pp)
return -ENOMEM;
// Seems that dmam_alloc_coherent() in Zynq does not really make it "coherent" (write buffers), but stream functions work
/*
Command Table Descriptor Base Address (CTBA): Indicates the 32-bit physical address of
the command table, which contains the command FIS, ATAPI Command, and PRD table. This
address must be aligned to a 128-byte cache line, indicated by bits 06:00 being reserved.
mem = dmam_alloc_coherent(dev, AHCI_CMD_TBL_AR_SZ, &mem_dma, GFP_KERNEL);
if (!mem)
return -ENOMEM;
memset(mem, 0, AHCI_CMD_TBL_AR_SZ); // dmam_alloc_coherent() does this
void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag);
dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);
void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction direction);
*/
// mem = devm_kzalloc(dev, AHCI_CMD_TBL_AR_SZ + align_cdt - 1, GFP_KERNEL);
// mem = devm_kmalloc(dev, AHCI_CMD_TBL_AR_SZ + align_cdt - 1, GFP_KERNEL); // let some junk be there
mem = devm_kmalloc(dev, 0x100000, GFP_KERNEL); // AHCI_CMD_TBL_AR_SZ = 0x16000
dbg_p = (u32*) mem;
for (dbg_i=0; dbg_i < ((AHCI_CMD_TBL_AR_SZ + align_cdt)>>2); dbg_i++) {
dbg_p[dbg_i] = dbg_i;
}
dbg_i = 0;
/*
if (((u32) mem) & (align_cdt - 1)) {
// mem += align_cdt - (((u32) mem) & (align_cdt - 1));
dbg_i = align_cdt - (((u32) mem) & (align_cdt - 1));
}
*/
mem_dma = dma_map_single(dev, mem, AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE); // maybe DMA_BIDIRECTIONAL, but currently we do not use DMA for received FISes
dev_info(dev, "ahci_elphel.c: dbg_i= 0x%08x, mem= 0x%08x, mem_dma= 0x%08x", dbg_i, (u32) mem, (u32) mem_dma);
pp->cmd_tbl = mem + dbg_i;
pp->cmd_tbl_dma = mem_dma + dbg_i;
dev_info(dev, "ahci_elphel.c: dbg_i= 0x%08x, pp->cmd_tbl= 0x%08x, pp->cmd_tbl_dma= 0x%08x", dbg_i, (u32) pp->cmd_tbl, (u32) pp->cmd_tbl_dma);
/*
* Set predefined addresses
*/
pp->cmd_slot = hpriv->mmio + dpriv->clb_offs;
//pp->cmd_slot_dma = virt_to_phys(pp->cmd_slot);
pp->cmd_slot_dma = 0x80000000 + dpriv->clb_offs;
pp->rx_fis = hpriv->mmio + dpriv->fb_offs;
//pp->rx_fis_dma = virt_to_phys(pp->rx_fis);
pp->rx_fis_dma = 0x80000000 + dpriv->fb_offs;
/*dev_info(dev, "cmd_slot: 0x%p", pp->cmd_slot);
dev_info(dev, "cmd_slot_dma: 0x%08u", pp->cmd_slot_dma);
dev_info(dev, "rx_fis: 0x%p", pp->rx_fis);
dev_info(dev, "rx_fis_dma: 0x%08u", pp->rx_fis_dma);
dev_info(dev, "base_addr: 0x%08u", dpriv->base_addr);*/
/*
* Save off initial list of interrupts to be enabled.
* This could be changed later
*/
pp->intr_mask = DEF_PORT_IRQ;
ap->private_data = pp;
// libahci_debug_state_dump(ap);
// libahci_debug_state_dump(ap);
//libahci_debug_saxigp1_save(ap, 0x3000);
//libahci_debug_saxigp1_save(ap, 0x3000);
dev_info(dev, "flags (ATA_FLAG_xxx): %u", ap->flags);
dev_info(dev, "pflags (ATA_PFLAG_xxx): %u", ap->pflags);
dev_info(dev, "ahci_elphel.c: Calling ahci_port_resume()");
return ahci_port_resume(ap);
}
static int elphel_parse_prop(const struct device_node *devn,
struct device *dev,
struct elphel_ahci_priv *dpriv)
{
u64 size;
unsigned int flags;
const __be32 *val;
struct resource res;
if (!devn) {
dev_err(dev, "device tree node is not found");
return -EINVAL;
}
val = of_get_property(devn, PROP_NAME_CLB_OFFS, NULL);
dpriv->clb_offs = be32_to_cpup(val);
val = of_get_property(devn, PROP_NAME_FB_OFFS, NULL);
dpriv->fb_offs = be32_to_cpup(val);
val = of_get_address(devn, 0, NULL, NULL);
if (val != NULL) {
dpriv->base_addr = be32_to_cpu(val);
dev_info(dev, "base_addr: 0x%08u", dpriv->base_addr);
} else {
dev_err(dev, "can not get register address");
}
//of_address_to_resource(devn, 0, &res);
return 0;
}
static int elphel_drv_probe(struct platform_device *pdev)
{
int ret;
struct ahci_host_priv *hpriv;
struct elphel_ahci_priv *drv_priv;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
struct resource *res;
unsigned int reg_val;
dev_info(&pdev->dev, "probing Elphel AHCI driver");
drv_priv = devm_kzalloc(dev, sizeof(struct elphel_ahci_priv), GFP_KERNEL);
if (!drv_priv)
return -ENOMEM;
match = of_match_device(ahci_elphel_of_match, &pdev->dev);
if (!match)
return -EINVAL;
ret = elphel_parse_prop(dev->of_node, dev, drv_priv);
if (ret != 0)
return ret;
hpriv = ahci_platform_get_resources(pdev);
if (IS_ERR(hpriv))
return PTR_ERR(hpriv);
hpriv->plat_data = drv_priv;
reg_val = readl(hpriv->mmio + HOST_CAP);
dev_info(dev, "HOST CAP register: 0x%08x", reg_val);
reg_val = readl(hpriv->mmio + HOST_CTL);
dev_info(dev, "HOST GHC register: 0x%08x", reg_val);
reg_val = readl(hpriv->mmio + HOST_IRQ_STAT);
dev_info(dev, "HOST IS register: 0x%08x", reg_val);
reg_val = readl(hpriv->mmio + HOST_PORTS_IMPL);
dev_info(dev, "HOST PI register: 0x%08x", reg_val);
reg_val = readl(hpriv->mmio + HOST_VERSION);
dev_info(dev, "HOST VS register: 0x%08x", reg_val);
phys_addr_t paddr = virt_to_phys(hpriv->mmio);
void *vaddr = phys_to_virt(paddr);
dev_err(dev, "current mmio virt addr: 0x%p\n", hpriv->mmio);
dev_err(dev, "current mmio virt addr as uint: 0x%08x\n", hpriv->mmio);
dev_err(dev, "mmio phys addr: 0x%08x\n", paddr);
dev_err(dev, "mmio phys addr as tr: 0x%p\n", paddr);
dev_err(dev, "back converted mmio virt addr: 0x%p\n", vaddr);
//printk(KERN_DEBUG, "current mmio virt addr: %p\n", hpriv->mmio);
//printk(KERN_DEBUG, "mmio phys addr: %u\n", paddr);
//printk(KERN_DEBUG, "back converted mmio virt addr: %p\n", vaddr);
printk(KERN_DEBUG "======");
ret = ahci_platform_init_host(pdev, hpriv, &ahci_elphel_port_info,
&ahci_platform_sht);
if (ret) {
dev_err(dev, "can not initialize platform host");
ahci_platform_disable_resources(hpriv);
return ret;
}
dev_info(dev, "ahci platform host initialized");
return 0;
}
static int elphel_drv_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "removing Elphel AHCI driver");
ata_platform_remove_one(pdev);
libahci_debug_exit();
return 0;
}
static unsigned int elphel_read_id(struct ata_device *dev, struct ata_taskfile *tf, u16 *id)
{
u32 err_mask;
struct device *d = &dev->tdev;
int i, len;
char *msg_str;
err_mask = ata_do_dev_read_id(dev, tf, id);
if (err_mask)
return err_mask;
dev_info(d, "elphel_read_id(): issue identify command finished\n");
/*dev_info(d, "dump IDENTIFY:\n");
msg_str = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!msg_str)
return 0;
len = 0;
for (i = 0; i < ATA_ID_WORDS; i++) {
if ((i % 16) == 0 && i != 0) {
dev_info(d, "%s\n", msg_str);
len = 0;
}
len += snprintf(msg_str + len, PAGE_SIZE - len, "0x%04x ", id[i]);
}
// print last string
dev_info(d, "%s\n", msg_str);*/
return 0;
}
static struct ata_port_operations ahci_elphel_ops = {
.inherits = &ahci_ops,
.port_start = elphel_port_start,
.read_id = elphel_read_id,
};
static const struct ata_port_info ahci_elphel_port_info = {
AHCI_HFLAGS(AHCI_HFLAG_NO_NCQ),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_elphel_ops,
};
static struct scsi_host_template ahci_platform_sht = {
AHCI_SHT(DRV_NAME),
.can_queue = 1,
.sg_tablesize = AHCI_MAX_SG,
.dma_boundary = AHCI_DMA_BOUNDARY,
.shost_attrs = ahci_shost_attrs,
.sdev_attrs = ahci_sdev_attrs,
};
static const struct of_device_id ahci_elphel_of_match[] = {
{ .compatible = "elphel,elphel-ahci", },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, ahci_elphel_of_match);
static struct platform_driver ahci_elphel_driver = {
.probe = elphel_drv_probe,
/*.remove = ata_platform_remove_one,*/
.remove = elphel_drv_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = ahci_elphel_of_match,
},
};
module_platform_driver(ahci_elphel_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Elphel, Inc.");
MODULE_DESCRIPTION("Elphel AHCI SATA platform driver for elphel393 camera");
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/ata/libahci.c 0000664 0000000 0000000 00000260654 12666203034 0025765 0 ustar 00root root 0000000 0000000 /*
* libahci.c - Common AHCI SATA low-level routines
*
* Maintained by: Tejun Heo
* Please ALWAYS copy linux-ide@vger.kernel.org
* on emails.
*
* Copyright 2004-2005 Red Hat, 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 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, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* libata documentation is available via 'make {ps|pdf}docs',
* as Documentation/DocBook/libata.*
*
* AHCI hardware documentation:
* http://www.intel.com/technology/serialata/pdf/rev1_0.pdf
* http://www.intel.com/technology/serialata/pdf/rev1_1.pdf
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ahci.h"
#include "libahci_debug.h"
#include "libata.h"
static int ahci_skip_host_reset;
int ahci_ignore_sss;
EXPORT_SYMBOL_GPL(ahci_ignore_sss);
module_param_named(skip_host_reset, ahci_skip_host_reset, int, 0444);
MODULE_PARM_DESC(skip_host_reset, "skip global host reset (0=don't skip, 1=skip)");
module_param_named(ignore_sss, ahci_ignore_sss, int, 0444);
MODULE_PARM_DESC(ignore_sss, "Ignore staggered spinup flag (0=don't ignore, 1=ignore)");
static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
unsigned hints);
static ssize_t ahci_led_show(struct ata_port *ap, char *buf);
static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
size_t size);
static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
ssize_t size);
static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
static int ahci_port_start(struct ata_port *ap);
static void ahci_port_stop(struct ata_port *ap);
static void ahci_qc_prep(struct ata_queued_cmd *qc);
static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc);
static void ahci_freeze(struct ata_port *ap);
static void ahci_thaw(struct ata_port *ap);
static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep);
static void ahci_enable_fbs(struct ata_port *ap);
static void ahci_disable_fbs(struct ata_port *ap);
static void ahci_pmp_attach(struct ata_port *ap);
static void ahci_pmp_detach(struct ata_port *ap);
static int ahci_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static void ahci_postreset(struct ata_link *link, unsigned int *class);
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
static void ahci_dev_config(struct ata_device *dev);
#ifdef CONFIG_PM
static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg);
#endif
static ssize_t ahci_activity_show(struct ata_device *dev, char *buf);
static ssize_t ahci_activity_store(struct ata_device *dev,
enum sw_activity val);
static void ahci_init_sw_activity(struct ata_link *link);
static ssize_t ahci_show_host_caps(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t ahci_show_host_cap2(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t ahci_show_host_version(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t ahci_show_port_cmd(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t ahci_read_em_buffer(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t ahci_store_em_buffer(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size);
static ssize_t ahci_show_em_supported(struct device *dev,
struct device_attribute *attr, char *buf);
static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL);
static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL);
static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL);
static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL);
static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO,
ahci_read_em_buffer, ahci_store_em_buffer);
static DEVICE_ATTR(em_message_supported, S_IRUGO, ahci_show_em_supported, NULL);
struct device_attribute *ahci_shost_attrs[] = {
&dev_attr_link_power_management_policy,
&dev_attr_em_message_type,
&dev_attr_em_message,
&dev_attr_ahci_host_caps,
&dev_attr_ahci_host_cap2,
&dev_attr_ahci_host_version,
&dev_attr_ahci_port_cmd,
&dev_attr_em_buffer,
&dev_attr_em_message_supported,
NULL
};
EXPORT_SYMBOL_GPL(ahci_shost_attrs);
struct device_attribute *ahci_sdev_attrs[] = {
&dev_attr_sw_activity,
&dev_attr_unload_heads,
NULL
};
EXPORT_SYMBOL_GPL(ahci_sdev_attrs);
struct ata_port_operations ahci_ops = {
.inherits = &sata_pmp_port_ops,
.qc_defer = ahci_pmp_qc_defer,
.qc_prep = ahci_qc_prep,
.qc_issue = ahci_qc_issue,
.qc_fill_rtf = ahci_qc_fill_rtf,
.freeze = ahci_freeze,
.thaw = ahci_thaw,
.softreset = ahci_softreset,
.hardreset = ahci_hardreset,
.postreset = ahci_postreset,
.pmp_softreset = ahci_softreset,
.error_handler = ahci_error_handler,
.post_internal_cmd = ahci_post_internal_cmd,
.dev_config = ahci_dev_config,
.scr_read = ahci_scr_read,
.scr_write = ahci_scr_write,
.pmp_attach = ahci_pmp_attach,
.pmp_detach = ahci_pmp_detach,
.set_lpm = ahci_set_lpm,
.em_show = ahci_led_show,
.em_store = ahci_led_store,
.sw_activity_show = ahci_activity_show,
.sw_activity_store = ahci_activity_store,
.transmit_led_message = ahci_transmit_led_message,
#ifdef CONFIG_PM
.port_suspend = ahci_port_suspend,
.port_resume = ahci_port_resume,
#endif
.port_start = ahci_port_start,
.port_stop = ahci_port_stop,
};
EXPORT_SYMBOL_GPL(ahci_ops);
struct ata_port_operations ahci_pmp_retry_srst_ops = {
.inherits = &ahci_ops,
.softreset = ahci_pmp_retry_softreset,
};
EXPORT_SYMBOL_GPL(ahci_pmp_retry_srst_ops);
static bool ahci_em_messages __read_mostly = true;
EXPORT_SYMBOL_GPL(ahci_em_messages);
module_param(ahci_em_messages, bool, 0444);
/* add other LED protocol types when they become supported */
MODULE_PARM_DESC(ahci_em_messages,
"AHCI Enclosure Management Message control (0 = off, 1 = on)");
/* device sleep idle timeout in ms */
static int devslp_idle_timeout __read_mostly = 1000;
module_param(devslp_idle_timeout, int, 0644);
MODULE_PARM_DESC(devslp_idle_timeout, "device sleep idle timeout");
static void ahci_enable_ahci(void __iomem *mmio)
{
int i;
u32 tmp;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
int len;
/* turn on AHCI_EN */
tmp = readl(mmio + HOST_CTL);
if (tmp & HOST_AHCI_EN) {// MSB (0x80000000) is already set (RO in x393_sata)
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_enable_ahci() - already enabled");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
msg_str = NULL;
}
return;
}
/* Some controllers need AHCI_EN to be written multiple times.
* Try a few times before giving up.
*/
for (i = 0; i < 5; i++) {
tmp |= HOST_AHCI_EN;
writel(tmp, mmio + HOST_CTL);
tmp = readl(mmio + HOST_CTL); /* flush && sanity check */
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "write HOST_AHCI_EN to HOST_CTL register");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
msg_str = NULL;
}
if (tmp & HOST_AHCI_EN)
return;
msleep(10);
}
WARN_ON(1);
}
static ssize_t ahci_show_host_caps(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
return sprintf(buf, "%x\n", hpriv->cap);
}
static ssize_t ahci_show_host_cap2(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
return sprintf(buf, "%x\n", hpriv->cap2);
}
static ssize_t ahci_show_host_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *mmio = hpriv->mmio;
return sprintf(buf, "%x\n", readl(mmio + HOST_VERSION));
}
static ssize_t ahci_show_port_cmd(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
void __iomem *port_mmio = ahci_port_base(ap);
return sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD));
}
static ssize_t ahci_read_em_buffer(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *mmio = hpriv->mmio;
void __iomem *em_mmio = mmio + hpriv->em_loc;
u32 em_ctl, msg;
unsigned long flags;
size_t count;
int i;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "read EM buffer");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
spin_lock_irqsave(ap->lock, flags);
em_ctl = readl(mmio + HOST_EM_CTL);
if (!(ap->flags & ATA_FLAG_EM) || em_ctl & EM_CTL_XMT ||
!(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO)) {
spin_unlock_irqrestore(ap->lock, flags);
return -EINVAL;
}
if (!(em_ctl & EM_CTL_MR)) {
spin_unlock_irqrestore(ap->lock, flags);
return -EAGAIN;
}
if (!(em_ctl & EM_CTL_SMB))
em_mmio += hpriv->em_buf_sz;
count = hpriv->em_buf_sz;
/* the count should not be larger than PAGE_SIZE */
if (count > PAGE_SIZE) {
if (printk_ratelimit())
ata_port_warn(ap,
"EM read buffer size too large: "
"buffer size %u, page size %lu\n",
hpriv->em_buf_sz, PAGE_SIZE);
count = PAGE_SIZE;
}
for (i = 0; i < count; i += 4) {
msg = readl(em_mmio + i);
buf[i] = msg & 0xff;
buf[i + 1] = (msg >> 8) & 0xff;
buf[i + 2] = (msg >> 16) & 0xff;
buf[i + 3] = (msg >> 24) & 0xff;
}
spin_unlock_irqrestore(ap->lock, flags);
return i;
}
static ssize_t ahci_store_em_buffer(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *mmio = hpriv->mmio;
void __iomem *em_mmio = mmio + hpriv->em_loc;
const unsigned char *msg_buf = buf;
u32 em_ctl, msg;
unsigned long flags;
int i;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "transmit message in EM buffer");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* check size validity */
if (!(ap->flags & ATA_FLAG_EM) ||
!(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO) ||
size % 4 || size > hpriv->em_buf_sz)
return -EINVAL;
spin_lock_irqsave(ap->lock, flags);
em_ctl = readl(mmio + HOST_EM_CTL);
if (em_ctl & EM_CTL_TM) {
spin_unlock_irqrestore(ap->lock, flags);
return -EBUSY;
}
for (i = 0; i < size; i += 4) {
msg = msg_buf[i] | msg_buf[i + 1] << 8 |
msg_buf[i + 2] << 16 | msg_buf[i + 3] << 24;
writel(msg, em_mmio + i);
}
writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);
spin_unlock_irqrestore(ap->lock, flags);
return size;
}
static ssize_t ahci_show_em_supported(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct ata_port *ap = ata_shost_to_port(shost);
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *mmio = hpriv->mmio;
u32 em_ctl;
em_ctl = readl(mmio + HOST_EM_CTL);
return sprintf(buf, "%s%s%s%s\n",
em_ctl & EM_CTL_LED ? "led " : "",
em_ctl & EM_CTL_SAFTE ? "saf-te " : "",
em_ctl & EM_CTL_SES ? "ses-2 " : "",
em_ctl & EM_CTL_SGPIO ? "sgpio " : "");
}
/**
* ahci_save_initial_config - Save and fixup initial config values
* @dev: target AHCI device
* @hpriv: host private area to store config values
*
* Some registers containing configuration info might be setup by
* BIOS and might be cleared on reset. This function saves the
* initial values of those registers into @hpriv such that they
* can be restored after controller reset.
*
* If inconsistent, config values are fixed up by this function.
*
* If it is not set already this function sets hpriv->start_engine to
* ahci_start_engine.
*
* LOCKING:
* None.
*/
void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
{
void __iomem *mmio = hpriv->mmio;
u32 cap, cap2, vers, port_map;
int i;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "read and save host capabilities");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
/* make sure AHCI mode is enabled before accessing CAP */
ahci_enable_ahci(mmio);
/* Values prefixed with saved_ are written back to host after
* reset. Values without are used for driver operation.
*/
hpriv->saved_cap = cap = readl(mmio + HOST_CAP);
hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL);
/* CAP2 register is only defined for AHCI 1.2 and later */
vers = readl(mmio + HOST_VERSION);
if ((vers >> 16) > 1 ||
((vers >> 16) == 1 && (vers & 0xFFFF) >= 0x200))
hpriv->saved_cap2 = cap2 = readl(mmio + HOST_CAP2);
else
hpriv->saved_cap2 = cap2 = 0;
/* some chips have errata preventing 64bit use */
if ((cap & HOST_CAP_64) && (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)) {
dev_info(dev, "controller can't do 64bit DMA, forcing 32bit\n");
cap &= ~HOST_CAP_64;
}
if ((cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_NO_NCQ)) {
dev_info(dev, "controller can't do NCQ, turning off CAP_NCQ\n");
cap &= ~HOST_CAP_NCQ;
}
if (!(cap & HOST_CAP_NCQ) && (hpriv->flags & AHCI_HFLAG_YES_NCQ)) {
dev_info(dev, "controller can do NCQ, turning on CAP_NCQ\n");
cap |= HOST_CAP_NCQ;
}
if ((cap & HOST_CAP_PMP) && (hpriv->flags & AHCI_HFLAG_NO_PMP)) {
dev_info(dev, "controller can't do PMP, turning off CAP_PMP\n");
cap &= ~HOST_CAP_PMP;
}
if ((cap & HOST_CAP_SNTF) && (hpriv->flags & AHCI_HFLAG_NO_SNTF)) {
dev_info(dev,
"controller can't do SNTF, turning off CAP_SNTF\n");
cap &= ~HOST_CAP_SNTF;
}
if ((cap2 & HOST_CAP2_SDS) && (hpriv->flags & AHCI_HFLAG_NO_DEVSLP)) {
dev_info(dev,
"controller can't do DEVSLP, turning off\n");
cap2 &= ~HOST_CAP2_SDS;
cap2 &= ~HOST_CAP2_SADM;
}
if (!(cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_YES_FBS)) {
dev_info(dev, "controller can do FBS, turning on CAP_FBS\n");
cap |= HOST_CAP_FBS;
}
if ((cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_NO_FBS)) {
dev_info(dev, "controller can't do FBS, turning off CAP_FBS\n");
cap &= ~HOST_CAP_FBS;
}
if (hpriv->force_port_map && port_map != hpriv->force_port_map) {
dev_info(dev, "forcing port_map 0x%x -> 0x%x\n",
port_map, hpriv->force_port_map);
port_map = hpriv->force_port_map;
}
if (hpriv->mask_port_map) {
dev_warn(dev, "masking port_map 0x%x -> 0x%x\n",
port_map,
port_map & hpriv->mask_port_map);
port_map &= hpriv->mask_port_map;
}
/* cross check port_map and cap.n_ports */
if (port_map) {
int map_ports = 0;
for (i = 0; i < AHCI_MAX_PORTS; i++)
if (port_map & (1 << i))
map_ports++;
/* If PI has more ports than n_ports, whine, clear
* port_map and let it be generated from n_ports.
*/
if (map_ports > ahci_nr_ports(cap)) {
dev_warn(dev,
"implemented port map (0x%x) contains more ports than nr_ports (%u), using nr_ports\n",
port_map, ahci_nr_ports(cap));
port_map = 0;
}
}
/* fabricate port_map from cap.nr_ports */
if (!port_map) {
port_map = (1 << ahci_nr_ports(cap)) - 1;
dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map);
/* write the fixed up value to the PI register */
hpriv->saved_port_map = port_map;
}
/* record values to use during operation */
hpriv->cap = cap;
hpriv->cap2 = cap2;
hpriv->port_map = port_map;
if (!hpriv->start_engine)
hpriv->start_engine = ahci_start_engine;
dev_warn(dev, "ahci_save_initial_config() done\n");
//ahci_save_initial_config
}
EXPORT_SYMBOL_GPL(ahci_save_initial_config);
/**
* ahci_restore_initial_config - Restore initial config
* @host: target ATA host
*
* Restore initial config stored by ahci_save_initial_config().
*
* LOCKING:
* None.
*/
static void ahci_restore_initial_config(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "restore host capabilities");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
writel(hpriv->saved_cap, mmio + HOST_CAP);
if (hpriv->saved_cap2)
writel(hpriv->saved_cap2, mmio + HOST_CAP2);
writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL);
(void) readl(mmio + HOST_PORTS_IMPL); /* flush */
}
static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg)
{
static const int offset[] = {
[SCR_STATUS] = PORT_SCR_STAT, // 0 -> 0x28
[SCR_CONTROL] = PORT_SCR_CTL, // 2 -> 0x2c
[SCR_ERROR] = PORT_SCR_ERR, // 1 -> 0x30
[SCR_ACTIVE] = PORT_SCR_ACT, // 3 -> 0x34
[SCR_NOTIFICATION] = PORT_SCR_NTF, // 4 -> 0x3c
};
struct ahci_host_priv *hpriv = ap->host->private_data;
if (sc_reg < ARRAY_SIZE(offset) &&
(sc_reg != SCR_NOTIFICATION || (hpriv->cap & HOST_CAP_SNTF)))
return offset[sc_reg];
return 0;
}
static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val)
{
void __iomem *port_mmio = ahci_port_base(link->ap);
int offset = ahci_scr_offset(link->ap, sc_reg);
int len;
u32 tmp;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_scr_read() port=%u SATA status and control registers %u", link->ap->port_no,sc_reg);
libahci_debug_event(link->ap, msg_str, len);
}
if (offset) {
*val = readl(port_mmio + offset);
if (msg_str != NULL) {
tmp = readl(port_mmio + PORT_CMD);
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tport %u offset: 0x%x, value: 0x%x (current PxCMD=0x%08x)", link->ap->port_no, offset, *val, tmp);
libahci_debug_event(link->ap, msg_str, len);
}
return 0;
}
if (msg_str != NULL)
kfree(msg_str);
return -EINVAL;
}
static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
{
void __iomem *port_mmio = ahci_port_base(link->ap);
int offset = ahci_scr_offset(link->ap, sc_reg);
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_scr_write(): port %u SATA status and control registers", link->ap->port_no);
libahci_debug_event(link->ap, msg_str, len);
}
if (offset) {
writel(val, port_mmio + offset);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tport %u offset: 0x%x, value: 0x%x", link->ap->port_no, offset, val);
libahci_debug_event(link->ap, msg_str, len);
}
return 0;
}
if (msg_str != NULL)
kfree(msg_str);
return -EINVAL;
}
void ahci_start_engine(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_start_engine() port %u set PxCMD.ST", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
dev_info(ap->host->dev, "ahci_start_engine()\n");
/* start DMA */
tmp = readl(port_mmio + PORT_CMD);
tmp |= PORT_CMD_START;
writel(tmp, port_mmio + PORT_CMD);
readl(port_mmio + PORT_CMD); /* flush */
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite to port %u register PxCMD, value: 0x%08x", ap->port_no, tmp);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
}
EXPORT_SYMBOL_GPL(ahci_start_engine);
int ahci_stop_engine(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_stop_engine(): clear PxCMD.ST port %u command list DMA engine", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
tmp = readl(port_mmio + PORT_CMD);
/* check if the HBA is idle */
if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0){
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tDMA was not running, no need to stop: (PxCMD=0x%08x)", tmp);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
return 0;
}
/* setting HBA to idle */
tmp &= ~PORT_CMD_START;
writel(tmp, port_mmio + PORT_CMD);
/* wait for engine to stop. This could be as long as 500 msec */
tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
if (tmp & PORT_CMD_LIST_ON) {
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\t FAILED waiting PORT_CMD_LIST_ON to clear, PxCMD, value: 0x%08x", tmp);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
return -EIO;
}
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\t SUCCESS waiting PORT_CMD_LIST_ON to clear, PxCMD, value: 0x%08x", tmp);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
return 0;
}
EXPORT_SYMBOL_GPL(ahci_stop_engine);
void ahci_start_fis_rx(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
u32 tmp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_start_fis_rx() port %u FIS RX reception (set PxCMD.FRE)", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* set FIS registers */
if (hpriv->cap & HOST_CAP_64)
writel((pp->cmd_slot_dma >> 16) >> 16,
port_mmio + PORT_LST_ADDR_HI);
writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
if (hpriv->cap & HOST_CAP_64)
writel((pp->rx_fis_dma >> 16) >> 16,
port_mmio + PORT_FIS_ADDR_HI);
writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
/* enable FIS reception */
tmp = readl(port_mmio + PORT_CMD);
tmp |= PORT_CMD_FIS_RX;
writel(tmp, port_mmio + PORT_CMD);
/* flush */
readl(port_mmio + PORT_CMD);
}
EXPORT_SYMBOL_GPL(ahci_start_fis_rx);
static int ahci_stop_fis_rx(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_stop_fis_rx port %u FIS RX reception (clear PxCMD.FRE)", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* disable FIS reception */
tmp = readl(port_mmio + PORT_CMD);
tmp &= ~PORT_CMD_FIS_RX;
writel(tmp, port_mmio + PORT_CMD);
/* wait for completion, spec says 500ms, give it 1000 */
tmp = ata_wait_register(ap, port_mmio + PORT_CMD, PORT_CMD_FIS_ON,
PORT_CMD_FIS_ON, 10, 1000);
// Currently x393_sata receives FISes to register memory, it may become RO, so ignore
#if 0
if (tmp & PORT_CMD_FIS_ON)
return -EBUSY;
#endif
return 0;
}
static void ahci_power_up(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 cmd;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_power_up() port %u wake up link", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
/* spin up device */
if (hpriv->cap & HOST_CAP_SSS) {
cmd |= PORT_CMD_SPIN_UP;
writel(cmd, port_mmio + PORT_CMD);
}
/* wake up link */
writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD);
}
static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
unsigned int hints)
{
struct ata_port *ap = link->ap;
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_set_lpm() set port %u link power management", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
if (policy != ATA_LPM_MAX_POWER) {
/*
* Disable interrupts on Phy Ready. This keeps us from
* getting woken up due to spurious phy ready
* interrupts.
*/
pp->intr_mask &= ~PORT_IRQ_PHYRDY;
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
sata_link_scr_lpm(link, policy, false);
}
if (hpriv->cap & HOST_CAP_ALPM) {
u32 cmd = readl(port_mmio + PORT_CMD);
if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) {
cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
cmd |= PORT_CMD_ICC_ACTIVE;
writel(cmd, port_mmio + PORT_CMD);
readl(port_mmio + PORT_CMD);
/* wait 10ms to be sure we've come out of LPM state */
ata_msleep(ap, 10);
} else {
cmd |= PORT_CMD_ALPE;
if (policy == ATA_LPM_MIN_POWER)
cmd |= PORT_CMD_ASP;
/* write out new cmd value */
writel(cmd, port_mmio + PORT_CMD);
}
}
/* set aggressive device sleep */
if ((hpriv->cap2 & HOST_CAP2_SDS) &&
(hpriv->cap2 & HOST_CAP2_SADM) &&
(link->device->flags & ATA_DFLAG_DEVSLP)) {
if (policy == ATA_LPM_MIN_POWER)
ahci_set_aggressive_devslp(ap, true);
else
ahci_set_aggressive_devslp(ap, false);
}
if (policy == ATA_LPM_MAX_POWER) {
sata_link_scr_lpm(link, policy, false);
/* turn PHYRDY IRQ back on */
pp->intr_mask |= PORT_IRQ_PHYRDY;
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
return 0;
}
#ifdef CONFIG_PM
static void ahci_power_down(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 cmd, scontrol;
if (!(hpriv->cap & HOST_CAP_SSS))
return;
/* put device into listen mode, first set PxSCTL.DET to 0 */
scontrol = readl(port_mmio + PORT_SCR_CTL);
scontrol &= ~0xf;
writel(scontrol, port_mmio + PORT_SCR_CTL);
/* then set PxCMD.SUD to 0 */
cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK;
cmd &= ~PORT_CMD_SPIN_UP;
writel(cmd, port_mmio + PORT_CMD);
}
#endif
static void ahci_start_port(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
struct ata_link *link;
struct ahci_em_priv *emp;
ssize_t rc;
int i;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_start_port(): port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* enable FIS reception */
ahci_start_fis_rx(ap);
/* enable DMA */
if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE))
hpriv->start_engine(ap);
/* turn on LEDs */
if (ap->flags & ATA_FLAG_EM) {
ata_for_each_link(link, ap, EDGE) {
emp = &pp->em_priv[link->pmp];
/* EM Transmit bit maybe busy during init */
for (i = 0; i < EM_MAX_RETRY; i++) {
rc = ap->ops->transmit_led_message(ap,
emp->led_state,
4);
/*
* If busy, give a breather but do not
* release EH ownership by using msleep()
* instead of ata_msleep(). EM Transmit
* bit is busy for the whole host and
* releasing ownership will cause other
* ports to fail the same way.
*/
if (rc == -EBUSY)
msleep(1);
else
break;
}
}
}
if (ap->flags & ATA_FLAG_SW_ACTIVITY)
ata_for_each_link(link, ap, EDGE)
ahci_init_sw_activity(link);
}
static int ahci_deinit_port(struct ata_port *ap, const char **emsg)
{
int rc;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_deinit_port():port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* disable DMA */
rc = ahci_stop_engine(ap);
if (rc) {
*emsg = "failed to stop engine";
return rc;
}
/* disable FIS reception */
rc = ahci_stop_fis_rx(ap);
if (rc) {
*emsg = "failed stop FIS RX";
return rc;
}
return 0;
}
int ahci_reset_controller(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
u32 tmp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_reset_controller()");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
/* we must be in AHCI mode, before using anything
* AHCI-specific, such as HOST_RESET.
*/
ahci_enable_ahci(mmio);
/* global controller reset */
if (!ahci_skip_host_reset) {
tmp = readl(mmio + HOST_CTL);
if ((tmp & HOST_RESET) == 0) {
writel(tmp | HOST_RESET, mmio + HOST_CTL);
readl(mmio + HOST_CTL); /* flush */
}
/*
* to perform host reset, OS should set HOST_RESET
* and poll until this bit is read to be "0".
* reset must complete within 1 second, or
* the hardware should be considered fried.
*/
tmp = ata_wait_register(NULL, mmio + HOST_CTL, HOST_RESET,
HOST_RESET, 10, 1000);
if (tmp & HOST_RESET) {
dev_err(host->dev, "controller reset failed (0x%x)\n",
tmp);
return -EIO;
}
/* turn on AHCI mode */
ahci_enable_ahci(mmio);
/* Some registers might be cleared on reset. Restore
* initial values.
*/
ahci_restore_initial_config(host);
} else
dev_info(host->dev, "skipping global host reset\n");
return 0;
}
EXPORT_SYMBOL_GPL(ahci_reset_controller);
static void ahci_sw_activity(struct ata_link *link)
{
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_sw_activity()");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
if (!(link->flags & ATA_LFLAG_SW_ACTIVITY))
return;
emp->activity++;
if (!timer_pending(&emp->timer))
mod_timer(&emp->timer, jiffies + msecs_to_jiffies(10));
}
static void ahci_sw_activity_blink(unsigned long arg)
{
struct ata_link *link = (struct ata_link *)arg;
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
unsigned long led_message = emp->led_state;
u32 activity_led_state;
unsigned long flags;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_sw_activity_blink()");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
led_message &= EM_MSG_LED_VALUE;
led_message |= ap->port_no | (link->pmp << 8);
/* check to see if we've had activity. If so,
* toggle state of LED and reset timer. If not,
* turn LED to desired idle state.
*/
spin_lock_irqsave(ap->lock, flags);
if (emp->saved_activity != emp->activity) {
emp->saved_activity = emp->activity;
/* get the current LED state */
activity_led_state = led_message & EM_MSG_LED_VALUE_ON;
if (activity_led_state)
activity_led_state = 0;
else
activity_led_state = 1;
/* clear old state */
led_message &= ~EM_MSG_LED_VALUE_ACTIVITY;
/* toggle state */
led_message |= (activity_led_state << 16);
mod_timer(&emp->timer, jiffies + msecs_to_jiffies(100));
} else {
/* switch to idle */
led_message &= ~EM_MSG_LED_VALUE_ACTIVITY;
if (emp->blink_policy == BLINK_OFF)
led_message |= (1 << 16);
}
spin_unlock_irqrestore(ap->lock, flags);
ap->ops->transmit_led_message(ap, led_message, 4);
}
static void ahci_init_sw_activity(struct ata_link *link)
{
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_init_sw_activity()");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
/* init activity stats, setup timer */
emp->saved_activity = emp->activity = 0;
setup_timer(&emp->timer, ahci_sw_activity_blink, (unsigned long)link);
/* check our blink policy and set flag for link if it's enabled */
if (emp->blink_policy)
link->flags |= ATA_LFLAG_SW_ACTIVITY;
}
int ahci_reset_em(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
u32 em_ctl;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_reset_em(): reset EM message logic");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
em_ctl = readl(mmio + HOST_EM_CTL);
if ((em_ctl & EM_CTL_TM) || (em_ctl & EM_CTL_RST))
return -EINVAL;
writel(em_ctl | EM_CTL_RST, mmio + HOST_EM_CTL);
return 0;
}
EXPORT_SYMBOL_GPL(ahci_reset_em);
static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
ssize_t size)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
void __iomem *mmio = hpriv->mmio;
u32 em_ctl;
u32 message[] = {0, 0};
unsigned long flags;
int pmp;
struct ahci_em_priv *emp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_transmit_led_message() send port %u LED message", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* get the slot number from the message */
pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
if (pmp < EM_MAX_SLOTS)
emp = &pp->em_priv[pmp];
else
return -EINVAL;
spin_lock_irqsave(ap->lock, flags);
/*
* if we are still busy transmitting a previous message,
* do not allow
*/
em_ctl = readl(mmio + HOST_EM_CTL);
if (em_ctl & EM_CTL_TM) {
spin_unlock_irqrestore(ap->lock, flags);
return -EBUSY;
}
if (hpriv->em_msg_type & EM_MSG_TYPE_LED) {
/*
* create message header - this is all zero except for
* the message size, which is 4 bytes.
*/
message[0] |= (4 << 8);
/* ignore 0:4 of byte zero, fill in port info yourself */
message[1] = ((state & ~EM_MSG_LED_HBA_PORT) | ap->port_no);
/* write message to EM_LOC */
writel(message[0], mmio + hpriv->em_loc);
writel(message[1], mmio + hpriv->em_loc+4);
/*
* tell hardware to transmit the message
*/
writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);
}
/* save off new led state for port/slot */
emp->led_state = state;
spin_unlock_irqrestore(ap->lock, flags);
return size;
}
static ssize_t ahci_led_show(struct ata_port *ap, char *buf)
{
struct ahci_port_priv *pp = ap->private_data;
struct ata_link *link;
struct ahci_em_priv *emp;
int rc = 0;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_led_show()");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
ata_for_each_link(link, ap, EDGE) {
emp = &pp->em_priv[link->pmp];
rc += sprintf(buf, "%lx\n", emp->led_state);
}
return rc;
}
static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
size_t size)
{
unsigned int state;
int pmp;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_led_store()");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
if (kstrtouint(buf, 0, &state) < 0)
return -EINVAL;
/* get the slot number from the message */
pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
if (pmp < EM_MAX_SLOTS)
emp = &pp->em_priv[pmp];
else
return -EINVAL;
/* mask off the activity bits if we are in sw_activity
* mode, user should turn off sw_activity before setting
* activity led through em_message
*/
if (emp->blink_policy)
state &= ~EM_MSG_LED_VALUE_ACTIVITY;
return ap->ops->transmit_led_message(ap, state, size);
}
static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val)
{
struct ata_link *link = dev->link;
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
u32 port_led_state = emp->led_state;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_activity_store()");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* save the desired Activity LED behavior */
if (val == OFF) {
/* clear LFLAG */
link->flags &= ~(ATA_LFLAG_SW_ACTIVITY);
/* set the LED to OFF */
port_led_state &= EM_MSG_LED_VALUE_OFF;
port_led_state |= (ap->port_no | (link->pmp << 8));
ap->ops->transmit_led_message(ap, port_led_state, 4);
} else {
link->flags |= ATA_LFLAG_SW_ACTIVITY;
if (val == BLINK_OFF) {
/* set LED to ON for idle */
port_led_state &= EM_MSG_LED_VALUE_OFF;
port_led_state |= (ap->port_no | (link->pmp << 8));
port_led_state |= EM_MSG_LED_VALUE_ON; /* check this */
ap->ops->transmit_led_message(ap, port_led_state, 4);
}
}
emp->blink_policy = val;
return 0;
}
static ssize_t ahci_activity_show(struct ata_device *dev, char *buf)
{
struct ata_link *link = dev->link;
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_em_priv *emp = &pp->em_priv[link->pmp];
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_activity_show()");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* display the saved value of activity behavior for this
* disk.
*/
return sprintf(buf, "%d\n", emp->blink_policy);
}
static void ahci_port_init(struct device *dev, struct ata_port *ap,
int port_no, void __iomem *mmio,
void __iomem *port_mmio)
{
const char *emsg = NULL;
int rc;
u32 tmp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_port_init() port %u init", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* make sure port is not active */
rc = ahci_deinit_port(ap, &emsg);
if (rc)
dev_warn(dev, "%s (%d)\n", emsg, rc);
/* clear SError */
tmp = readl(port_mmio + PORT_SCR_ERR);
VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);
writel(tmp, port_mmio + PORT_SCR_ERR);
/* clear port IRQ */
tmp = readl(port_mmio + PORT_IRQ_STAT);
VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
if (tmp)
writel(tmp, port_mmio + PORT_IRQ_STAT);
writel(1 << port_no, mmio + HOST_IRQ_STAT);
}
void ahci_init_controller(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
int i;
void __iomem *port_mmio;
u32 tmp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_init_controller() ... and all enable ports");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
port_mmio = ahci_port_base(ap);
if (ata_port_is_dummy(ap))
continue;
ahci_port_init(host->dev, ap, i, mmio, port_mmio);
}
tmp = readl(mmio + HOST_CTL);
VPRINTK("HOST_CTL 0x%x\n", tmp);
writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
tmp = readl(mmio + HOST_CTL);
VPRINTK("HOST_CTL 0x%x\n", tmp);
}
EXPORT_SYMBOL_GPL(ahci_init_controller);
static void ahci_dev_config(struct ata_device *dev)
{
struct ahci_host_priv *hpriv = dev->link->ap->host->private_data;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_dev_config()");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
if (hpriv->flags & AHCI_HFLAG_SECT255) {
dev->max_sectors = 255;
ata_dev_info(dev,
"SB600 AHCI: limiting to 255 sectors per cmd\n");
}
}
unsigned int ahci_dev_classify(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ata_taskfile tf;
u32 tmp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
tmp = readl(port_mmio + PORT_SIG);
tf.lbah = (tmp >> 24) & 0xff;
tf.lbam = (tmp >> 16) & 0xff;
tf.lbal = (tmp >> 8) & 0xff;
tf.nsect = (tmp) & 0xff;
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_dev_classify(): reading port %u signature: PxSIG = 0x%08x", ap->port_no, tmp);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
return ata_dev_classify(&tf);
}
EXPORT_SYMBOL_GPL(ahci_dev_classify);
void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
u32 opts)
{
dma_addr_t cmd_tbl_dma;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
cmd_tbl_dma = pp->cmd_tbl_dma + tag * AHCI_CMD_TBL_SZ;
pp->cmd_slot[tag].opts = cpu_to_le32(opts);
pp->cmd_slot[tag].status = 0;
// pp->cmd_slot[tag].tbl_addr = cpu_to_le32(cmd_tbl_dma & 0xffffffff);
// Elphel - JUST TEMPORARILY
// pp->cmd_slot[tag].tbl_addr = cpu_to_le32(cmd_tbl_dma & 0xffffff80);
// pp->cmd_slot[tag].tbl_addr = cpu_to_le32((cmd_tbl_dma & 0xffffffff) +64); // All data correct
pp->cmd_slot[tag].tbl_addr = cpu_to_le32((cmd_tbl_dma & 0xffffffff)); // All data correct
pp->cmd_slot[tag].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tfill command slot %u: DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x, DW3 = 0x%08x",
tag, pp->cmd_slot[tag].opts, pp->cmd_slot[tag].status, pp->cmd_slot[tag].tbl_addr, pp->cmd_slot[tag].tbl_addr_hi);
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
}
EXPORT_SYMBOL_GPL(ahci_fill_cmd_slot);
int ahci_kick_engine(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_host_priv *hpriv = ap->host->private_data;
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
u32 tmp;
int busy, rc;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_kick_engine() kick port %u DMA engine", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
/* stop engine */
rc = ahci_stop_engine(ap);
if (rc)
goto out_restart;
/* need to do CLO?
* always do CLO if PMP is attached (AHCI-1.3 9.2)
*/
busy = status & (ATA_BUSY | ATA_DRQ);
if (!busy && !sata_pmp_attached(ap)) {
rc = 0;
goto out_restart;
}
if (!(hpriv->cap & HOST_CAP_CLO)) { // 0x1000000 supports command list override (x393_sata - not)
rc = -EOPNOTSUPP;
goto out_restart;
}
/* perform CLO */
tmp = readl(port_mmio + PORT_CMD);
tmp |= PORT_CMD_CLO;
writel(tmp, port_mmio + PORT_CMD);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite port %u register PxCMD, value: 0x%08x", ap->port_no, tmp);
libahci_debug_event(ap, msg_str, len);
}
rc = 0;
tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
PORT_CMD_CLO, PORT_CMD_CLO, 1, 500);
if (tmp & PORT_CMD_CLO)
rc = -EIO;
/* restart engine */
out_restart:
hpriv->start_engine(ap);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tahci_kick_engine() will return %d, busy was 0x%08x", rc, busy);
libahci_debug_event(ap, msg_str, len);
}
kfree(msg_str);
return rc;
}
EXPORT_SYMBOL_GPL(ahci_kick_engine);
static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
struct ata_taskfile *tf, int is_cmd, u16 flags,
unsigned long timeout_msec)
{
const u32 cmd_fis_len = 5; /* five dwords */
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u8 *fis = pp->cmd_tbl;
u32 tmp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_exec_polled_cmd(), port %u command issue", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* prep the command */
ata_tf_to_fis(tf, pmp, is_cmd, fis);
ahci_fill_cmd_slot(pp, 0, cmd_fis_len | flags | (pmp << 12));
libahci_debug_dump_region(ap, (const u32 *)fis, cmd_fis_len, "\twrite H2D register FIS; dump: ");
/* issue & wait */
writel(1, port_mmio + PORT_CMD_ISSUE);
if (timeout_msec) {
tmp = ata_wait_register(ap, port_mmio + PORT_CMD_ISSUE,
0x1, 0x1, 1, timeout_msec);
if (tmp & 0x1) {
ahci_kick_engine(ap);
return -EBUSY;
}
} else
readl(port_mmio + PORT_CMD_ISSUE); /* flush */
return 0;
}
int ahci_do_softreset(struct ata_link *link, unsigned int *class,
int pmp, unsigned long deadline,
int (*check_ready)(struct ata_link *link))
{
struct ata_port *ap = link->ap;
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
const char *reason = NULL;
unsigned long now, msecs;
struct ata_taskfile tf;
bool fbs_disabled = false;
int rc;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
dev_info(ap->host->dev, "ahci_do_softreset()");
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_do_softreset(): port %u softreset (complex actions follow)", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
DPRINTK("ENTER\n");
/* prepare for SRST (AHCI-1.1 10.4.1) */
rc = ahci_kick_engine(ap);
if (rc && rc != -EOPNOTSUPP)
ata_link_warn(link, "failed to reset engine (errno=%d)\n", rc);
/*
* According to AHCI-1.2 9.3.9: if FBS is enable, software shall
* clear PxFBS.EN to '0' prior to issuing software reset to devices
* that is attached to port multiplier.
*/
if (!ata_is_host_link(link) && pp->fbs_enabled) {
ahci_disable_fbs(ap);
fbs_disabled = true;
}
ata_tf_init(link->device, &tf);
/* issue the first D2H Register FIS */
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "issue the first H2D Register FIS; set ATA_SRST bit");
libahci_debug_event(ap, msg_str, len);
}
msecs = 0;
now = jiffies;
if (time_after(deadline, now))
msecs = jiffies_to_msecs(deadline - now);
tf.ctl |= ATA_SRST;
if (ahci_exec_polled_cmd(ap, pmp, &tf, 0,
AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY, msecs)) {
rc = -EIO;
reason = "1st FIS failed";
goto fail;
}
/* spec says at least 5us, but be generous and sleep for 1ms */
ata_msleep(ap, 1);
/* issue the second D2H Register FIS */
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "issue the second H2D Register FIS; reset ATA_SRST");
libahci_debug_event(ap, msg_str, len);
}
tf.ctl &= ~ATA_SRST;
ahci_exec_polled_cmd(ap, pmp, &tf, 0, 0, 0);
/* wait for link to become ready */
rc = ata_wait_after_reset(link, deadline, check_ready);
if (rc == -EBUSY && hpriv->flags & AHCI_HFLAG_SRST_TOUT_IS_OFFLINE) {
/*
* Workaround for cases where link online status can't
* be trusted. Treat device readiness timeout as link
* offline.
*/
ata_link_info(link, "device not ready, treating as offline\n");
*class = ATA_DEV_NONE;
} else if (rc) {
/* link occupied, -ENODEV too is an error */
reason = "device not ready";
goto fail;
} else
*class = ahci_dev_classify(ap);
/* re-enable FBS if disabled before */
if (fbs_disabled)
ahci_enable_fbs(ap);
if (msg_str != NULL) {
kfree(msg_str);
}
DPRINTK("EXIT, class=%u\n", *class);
return 0;
fail:
ata_link_err(link, "softreset failed (%s)\n", reason);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tsoftreset failed, reason code: %s", reason);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
return rc;
}
int ahci_check_ready(struct ata_link *link)
{
void __iomem *port_mmio = ahci_port_base(link->ap);
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
int len;
char *msg_str;
msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_check_ready(): read port %u Task File Data: PxTFD = 0x%02x, ata_check_ready()->%d", link->ap->port_no, status,ata_check_ready(status));
libahci_debug_event(link->ap, msg_str, len);
kfree(msg_str);
}
return ata_check_ready(status);
}
EXPORT_SYMBOL_GPL(ahci_check_ready);
static int ahci_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
int pmp = sata_srst_pmp(link);
int len;
char *msg_str;
msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_softreset()");
libahci_debug_event(link->ap, msg_str, len);
kfree(msg_str);
}
DPRINTK("ENTER\n");
return ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready);
}
EXPORT_SYMBOL_GPL(ahci_do_softreset);
static int ahci_bad_pmp_check_ready(struct ata_link *link)
{
void __iomem *port_mmio = ahci_port_base(link->ap);
u8 status = readl(port_mmio + PORT_TFDATA) & 0xFF;
u32 irq_status = readl(port_mmio + PORT_IRQ_STAT);
int len;
char *msg_str;
msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_bad_pmp_check_ready()");
libahci_debug_event(link->ap, msg_str, len);
kfree(msg_str);
}
/*
* There is no need to check TFDATA if BAD PMP is found due to HW bug,
* which can save timeout delay.
*/
if (irq_status & PORT_IRQ_BAD_PMP)
return -EIO;
return ata_check_ready(status);
}
static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
struct ata_port *ap = link->ap;
void __iomem *port_mmio = ahci_port_base(ap);
int pmp = sata_srst_pmp(link);
int rc;
u32 irq_sts;
int len;
char *msg_str;
msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_pmp_retry_softreset()");
libahci_debug_event(link->ap, msg_str, len);
kfree(msg_str);
}
DPRINTK("ENTER\n");
rc = ahci_do_softreset(link, class, pmp, deadline,
ahci_bad_pmp_check_ready);
/*
* Soft reset fails with IPMS set when PMP is enabled but
* SATA HDD/ODD is connected to SATA port, do soft reset
* again to port 0.
*/
if (rc == -EIO) {
irq_sts = readl(port_mmio + PORT_IRQ_STAT);
if (irq_sts & PORT_IRQ_BAD_PMP) {
ata_link_warn(link,
"applying PMP SRST workaround "
"and retrying\n");
rc = ahci_do_softreset(link, class, 0, deadline,
ahci_check_ready);
}
}
return rc;
}
static int ahci_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
struct ata_port *ap = link->ap;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
struct ata_taskfile tf;
bool online;
int rc;
dev_info(ap->host->dev, "ahci_hardreset()");
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_hardreset(): port %u hardreset", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
DPRINTK("ENTER\n");
ahci_stop_engine(ap);
/* clear D2H reception area to properly wait for D2H FIS */
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tclear RX FIS area");
libahci_debug_event(ap, msg_str, len);
}
ata_tf_init(link->device, &tf);
tf.command = ATA_BUSY;
ata_tf_to_fis(&tf, 0, 0, d2h_fis);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tproceed to sata_link_hardreset");
libahci_debug_event(ap, msg_str, len);
}
rc = sata_link_hardreset(link, timing, deadline, &online,
ahci_check_ready);
hpriv->start_engine(ap);
if (online) {
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tlink is online");
libahci_debug_event(ap, msg_str, len);
}
*class = ahci_dev_classify(ap);
}
if (msg_str != NULL) {
kfree(msg_str);
}
DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
return rc;
}
static void ahci_postreset(struct ata_link *link, unsigned int *class)
{
struct ata_port *ap = link->ap;
void __iomem *port_mmio = ahci_port_base(ap);
u32 new_tmp, tmp;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_postreset(): port %u postreset actions", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
ata_std_postreset(link, class);
/* Make sure port's ATAPI bit is set appropriately */
new_tmp = tmp = readl(port_mmio + PORT_CMD);
if (*class == ATA_DEV_ATAPI)
new_tmp |= PORT_CMD_ATAPI;
else
new_tmp &= ~PORT_CMD_ATAPI;
if (new_tmp != tmp) {
writel(new_tmp, port_mmio + PORT_CMD);
readl(port_mmio + PORT_CMD); /* flush */
}
}
static unsigned int ahci_fill_sg(struct ata_queued_cmd *qc, void *cmd_tbl)
{
struct scatterlist *sg;
struct ahci_sg *ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ;
unsigned int si;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
VPRINTK("ENTER\n");
/*
* Next, the S/G list.
*/
for_each_sg(qc->sg, sg, qc->n_elem, si) {
dma_addr_t addr = sg_dma_address(sg);
u32 sg_len = sg_dma_len(sg);
ahci_sg[si].addr = cpu_to_le32(addr & 0xffffffff);
ahci_sg[si].addr_hi = cpu_to_le32((addr >> 16) >> 16);
ahci_sg[si].flags_size = cpu_to_le32(sg_len - 1);
}
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\tfill S/G list for port %u: %u PRD(s) written", qc->ap->port_no, si);
libahci_debug_event(qc->ap, msg_str, len);
libahci_debug_dump_sg(qc, "reading data pointed by S/G list; dump: ");
kfree(msg_str);
}
return si;
}
static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct ahci_port_priv *pp = ap->private_data;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_pmp_qc_defer(): process qc_defer ata command for port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
if (!sata_pmp_attached(ap) || pp->fbs_enabled)
return ata_std_qc_defer(qc);
else
return sata_pmp_qc_defer_cmd_switch(qc);
}
static void ahci_qc_prep(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct ahci_port_priv *pp = ap->private_data;
int is_atapi = ata_is_atapi(qc->tf.protocol);
void *cmd_tbl;
u32 opts;
const u32 cmd_fis_len = 5; /* five dwords */
unsigned int n_elem;
u32 * elphel_dbg_ptr;
u32 elhel_dbg_buf[128];
int elphel_i;
// elphel test: set tag = 0, change to qc->tag in functions below
//unsigned int tag = 0;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_qc_prep(): prepare command table information for port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/*
* Fill in command table information. First, the header,
* a SATA Register - Host to Device command FIS.
*/
// Elphel; Need to wrap with dma_sync_single_for_cpu / dma_sync_single_for_device, move to in ahci_elphel.c
// dma_map_single(dev, mem, AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE);
// void dma_sync_single_for_cpu(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);
// dma_sync_single_for_cpu(qc->dev, pp->cmd_tbl_dma - 4096, AHCI_CMD_TBL_AR_SZ + 4096, DMA_TO_DEVICE);
dma_sync_single_for_cpu(qc->dev, pp->cmd_tbl_dma, AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE);
cmd_tbl = pp->cmd_tbl + qc->tag * AHCI_CMD_TBL_SZ;
//cmd_tbl = pp->cmd_tbl + tag * AHCI_CMD_TBL_SZ;
// elphel_dbg_ptr = (u32*) cmd_tbl;
ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, cmd_tbl);
/*
for (elphel_i = 0; elphel_i < 128; elphel_i++){
elhel_dbg_buf[elphel_i] = elphel_i + (elphel_i << 8);
}
ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, (u8*) elhel_dbg_buf);
for (elphel_i = 0; elphel_i < 128; elphel_i++){
elphel_dbg_ptr[elphel_i] = elhel_dbg_buf[elphel_i];
}
// memset(cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32); // Elphel - just to write smth.
elphel_dbg_ptr[1] = 1;
elphel_dbg_ptr[2] = 2;
elphel_dbg_ptr[3] = 3;
elphel_dbg_ptr[4] = 4;
elphel_dbg_ptr[5] = 5;
elphel_dbg_ptr[6] = 6;
elphel_dbg_ptr[7] = 7;
elphel_dbg_ptr[8] = 8;
*/
if (is_atapi) {
memset(cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32);
memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len);
// libahci_debug_dump_region(ap, (const u32 *)(cmd_tbl + AHCI_CMD_TBL_CDB), 4, "\tthis is ATAPI command, dump ACMD region: ");
}
libahci_debug_dump_region(ap, (const u32 *)cmd_tbl, cmd_fis_len, "\twrite H2D register FIS; dump: ");
n_elem = 0;
if (qc->flags & ATA_QCFLAG_DMAMAP) {
n_elem = ahci_fill_sg(qc, cmd_tbl);
if (qc->dma_dir == DMA_TO_DEVICE) {
dev_info(&qc->dev->tdev, "%s: dma_sync_sg_for_device, qc->dma_dir: %d", __func__, qc->dma_dir);
dma_sync_sg_for_device(&qc->dev->tdev, qc->sg, qc->n_elem, qc->dma_dir);
} else if (qc->dma_dir == DMA_FROM_DEVICE) {
dev_info(&qc->dev->tdev, "%s: dma_sync_sg_for_cpu, qc->dma_dir: %d", __func__, qc->dma_dir);
dma_sync_sg_for_cpu(&qc->dev->tdev, qc->sg, qc->n_elem, qc->dma_dir);
}
}
/*
* Fill in command slot information.
*/
opts = cmd_fis_len | n_elem << 16 | (qc->dev->link->pmp << 12);
if (qc->tf.flags & ATA_TFLAG_WRITE)
opts |= AHCI_CMD_WRITE;
if (is_atapi)
opts |= AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH;
ahci_fill_cmd_slot(pp, qc->tag, opts);
//ahci_fill_cmd_slot(pp, tag, opts);
// Elphel move to ahci_elphel.c. See if the SG dma should also be handed to dma here
dma_sync_single_for_device(qc->dev, pp->cmd_tbl_dma, AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE);
// dma_sync_single_for_device(qc->dev, pp->cmd_tbl_dma - 4096, AHCI_CMD_TBL_AR_SZ+4096, DMA_TO_DEVICE);
// dma_sync_single_for_cpu (qc->dev, pp->cmd_tbl_dma, AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE);
// dma_sync_single_for_device(qc->dev, pp->cmd_tbl_dma, AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE);
// dma_sync_single_for_device(qc->dev, pp->cmd_tbl_dma - 0xff0, AHCI_CMD_TBL_AR_SZ+ 0xff0, DMA_TO_DEVICE);
}
static void ahci_fbs_dec_intr(struct ata_port *ap)
{
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 fbs = readl(port_mmio + PORT_FBS);
int retries = 3;
DPRINTK("ENTER\n");
BUG_ON(!pp->fbs_enabled);
/* time to wait for DEC is not specified by AHCI spec,
* add a retry loop for safety.
*/
writel(fbs | PORT_FBS_DEC, port_mmio + PORT_FBS);
fbs = readl(port_mmio + PORT_FBS);
while ((fbs & PORT_FBS_DEC) && retries--) {
udelay(1);
fbs = readl(port_mmio + PORT_FBS);
}
if (fbs & PORT_FBS_DEC)
dev_err(ap->host->dev, "failed to clear device error\n");
}
static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
struct ata_eh_info *host_ehi = &ap->link.eh_info;
struct ata_link *link = NULL;
struct ata_queued_cmd *active_qc;
struct ata_eh_info *active_ehi;
bool fbs_need_dec = false;
u32 serror;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
/* determine active link with error */
if (pp->fbs_enabled) {
void __iomem *port_mmio = ahci_port_base(ap);
u32 fbs = readl(port_mmio + PORT_FBS);
int pmp = fbs >> PORT_FBS_DWE_OFFSET;
if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links)) {
link = &ap->pmp_link[pmp];
fbs_need_dec = true;
}
} else
ata_for_each_link(link, ap, EDGE)
if (ata_link_active(link))
break;
if (!link)
link = &ap->link;
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_error_intr(): handle port %u error, irq_stat=0x%08x, SERR=0x%08x",
link->ap->port_no,irq_stat, ahci_scr_read(&ap->link, SCR_ERROR, &serror));
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
active_qc = ata_qc_from_tag(ap, link->active_tag);
active_ehi = &link->eh_info;
/* record irq stat */
ata_ehi_clear_desc(host_ehi);
ata_ehi_push_desc(host_ehi, "irq_stat 0x%08x", irq_stat);
/* AHCI needs SError cleared; otherwise, it might lock up */
ahci_scr_read(&ap->link, SCR_ERROR, &serror);
ahci_scr_write(&ap->link, SCR_ERROR, serror);
host_ehi->serror |= serror;
/* some controllers set IRQ_IF_ERR on device errors, ignore it */
if (hpriv->flags & AHCI_HFLAG_IGN_IRQ_IF_ERR)
irq_stat &= ~PORT_IRQ_IF_ERR;
if (irq_stat & PORT_IRQ_TF_ERR) {
/* If qc is active, charge it; otherwise, the active
* link. There's no active qc on NCQ errors. It will
* be determined by EH by reading log page 10h.
*/
if (active_qc)
active_qc->err_mask |= AC_ERR_DEV;
else
active_ehi->err_mask |= AC_ERR_DEV;
if (hpriv->flags & AHCI_HFLAG_IGN_SERR_INTERNAL)
host_ehi->serror &= ~SERR_INTERNAL;
}
if (irq_stat & PORT_IRQ_UNK_FIS) {
u32 *unk = pp->rx_fis + RX_FIS_UNK;
active_ehi->err_mask |= AC_ERR_HSM;
active_ehi->action |= ATA_EH_RESET;
ata_ehi_push_desc(active_ehi,
"unknown FIS %08x %08x %08x %08x" ,
unk[0], unk[1], unk[2], unk[3]);
}
if (sata_pmp_attached(ap) && (irq_stat & PORT_IRQ_BAD_PMP)) {
active_ehi->err_mask |= AC_ERR_HSM;
active_ehi->action |= ATA_EH_RESET;
ata_ehi_push_desc(active_ehi, "incorrect PMP");
}
if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) {
host_ehi->err_mask |= AC_ERR_HOST_BUS;
host_ehi->action |= ATA_EH_RESET;
ata_ehi_push_desc(host_ehi, "host bus error");
}
if (irq_stat & PORT_IRQ_IF_ERR) {
if (fbs_need_dec)
active_ehi->err_mask |= AC_ERR_DEV;
else {
host_ehi->err_mask |= AC_ERR_ATA_BUS;
host_ehi->action |= ATA_EH_RESET;
}
ata_ehi_push_desc(host_ehi, "interface fatal error");
}
if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) {
ata_ehi_hotplugged(host_ehi);
ata_ehi_push_desc(host_ehi, "%s",
irq_stat & PORT_IRQ_CONNECT ?
"connection status changed" : "PHY RDY changed");
}
/* okay, let's hand over to EH */
if (irq_stat & PORT_IRQ_FREEZE) //0x28c00050
ata_port_freeze(ap);
else if (fbs_need_dec) {
ata_link_abort(link);
ahci_fbs_dec_intr(ap);
} else
ata_port_abort(ap);
}
static void ahci_handle_port_interrupt(struct ata_port *ap,
void __iomem *port_mmio, u32 status)
{
struct ata_eh_info *ehi = &ap->link.eh_info;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
u32 qc_active = 0;
int rc;
int len;
char *msg = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "ahci_handle_port_interrupt(): port %u interrupt handler, PxIS: 0x%x", ap->port_no, status);
libahci_debug_event(ap, msg, len);
libahci_debug_dump_irq(status);
libahci_debug_irq_notify(ap);
}
/* ignore BAD_PMP while resetting */
if (unlikely(resetting))
status &= ~PORT_IRQ_BAD_PMP;
/* if LPM is enabled, PHYRDY doesn't mean anything */
if (ap->link.lpm_policy > ATA_LPM_MAX_POWER) {
status &= ~PORT_IRQ_PHYRDY;
ahci_scr_write(&ap->link, SCR_ERROR, SERR_PHYRDY_CHG);
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "resetting PORT_IRQ_PHYRDY, new PxIS: 0x%x", status);
libahci_debug_event(ap, msg, len);
}
}
if (unlikely(status & PORT_IRQ_ERROR)) {
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "processing PORT_IRQ_ERROR");
libahci_debug_event(ap, msg, len);
}
ahci_error_intr(ap, status);
return;
}
if (status & PORT_IRQ_SDB_FIS) {
/* If SNotification is available, leave notification
* handling to sata_async_notification(). If not,
* emulate it by snooping SDB FIS RX area.
*
* Snooping FIS RX area is probably cheaper than
* poking SNotification but some constrollers which
* implement SNotification, ICH9 for example, don't
* store AN SDB FIS into receive area.
*/
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "processing PORT_IRQ_SDB_FIS, further processing will be done on sata layer");
libahci_debug_event(ap, msg, len);
}
if (hpriv->cap & HOST_CAP_SNTF) {
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "host supports SNotification register, proceed to 'sata_async_notification'");
libahci_debug_event(ap, msg, len);
}
sata_async_notification(ap);
}
else {
/* If the 'N' bit in word 0 of the FIS is set,
* we just received asynchronous notification.
* Tell libata about it.
*
* Lack of SNotification should not appear in
* ahci 1.2, so the workaround is unnecessary
* when FBS is enabled.
*/
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "host DOES NOT support SNotification register, snoop FIS RX area and proceed to 'sata_async_notification'");
libahci_debug_event(ap, msg, len);
}
if (pp->fbs_enabled)
WARN_ON_ONCE(1);
else {
const __le32 *f = pp->rx_fis + RX_FIS_SDB;
u32 f0 = le32_to_cpu(f[0]);
if (f0 & (1 << 15))
sata_async_notification(ap);
}
}
}
/* pp->active_link is not reliable once FBS is enabled, both
* PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because
* NCQ and non-NCQ commands may be in flight at the same time.
*/
if (pp->fbs_enabled) {
if (ap->qc_active) {
qc_active = readl(port_mmio + PORT_SCR_ACT);
qc_active |= readl(port_mmio + PORT_CMD_ISSUE);
}
} else {
/* pp->active_link is valid iff any command is in flight */
if (ap->qc_active && pp->active_link->sactive)
qc_active = readl(port_mmio + PORT_SCR_ACT);
else
qc_active = readl(port_mmio + PORT_CMD_ISSUE);
}
if (msg != NULL) {
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "read port %u Serial ATA Active register, PxSACT: 0x%x", ap->port_no, qc_active);
libahci_debug_event(ap, msg, len);
len = snprintf(msg, LIBAHCI_DEBUG_BUFSZ, "proceed to 'ata_qc_complete_multiple'");
libahci_debug_event(ap, msg, len);
//libahci_debug_irq_notify(ap);
kfree(msg);
}
rc = ata_qc_complete_multiple(ap, qc_active);
/* while resetting, invalid completions are expected */
if (unlikely(rc < 0 && !resetting)) {
ehi->err_mask |= AC_ERR_HSM;
ehi->action |= ATA_EH_RESET;
ata_port_freeze(ap);
}
}
static void ahci_port_intr(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
u32 status;
status = readl(port_mmio + PORT_IRQ_STAT);
writel(status, port_mmio + PORT_IRQ_STAT);
ahci_handle_port_interrupt(ap, port_mmio, status);
}
static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
{
struct ata_port *ap = dev_instance;
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 status;
status = atomic_xchg(&pp->intr_status, 0);
if (!status)
return IRQ_NONE;
spin_lock_bh(ap->lock);
ahci_handle_port_interrupt(ap, port_mmio, status);
spin_unlock_bh(ap->lock);
return IRQ_HANDLED;
}
static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
{
struct ata_port *ap = dev_instance;
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_port_priv *pp = ap->private_data;
u32 status;
VPRINTK("ENTER\n");
status = readl(port_mmio + PORT_IRQ_STAT);
writel(status, port_mmio + PORT_IRQ_STAT);
atomic_or(status, &pp->intr_status);
VPRINTK("EXIT\n");
return IRQ_WAKE_THREAD;
}
static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
{
struct ata_host *host = dev_instance;
struct ahci_host_priv *hpriv;
unsigned int i, handled = 0;
void __iomem *mmio;
u32 irq_stat, irq_masked;
VPRINTK("ENTER\n");
hpriv = host->private_data;
mmio = hpriv->mmio;
/* sigh. 0xffffffff is a valid return from h/w */
irq_stat = readl(mmio + HOST_IRQ_STAT);
if (!irq_stat)
return IRQ_NONE;
irq_masked = irq_stat & hpriv->port_map;
spin_lock(&host->lock);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap;
if (!(irq_masked & (1 << i)))
continue;
ap = host->ports[i];
if (ap) {
ahci_port_intr(ap);
VPRINTK("port %u\n", i);
} else {
VPRINTK("port %u (no irq)\n", i);
if (ata_ratelimit())
dev_warn(host->dev,
"interrupt on disabled port %u\n", i);
}
handled = 1;
}
/* HOST_IRQ_STAT behaves as level triggered latch meaning that
* it should be cleared after all the port events are cleared;
* otherwise, it will raise a spurious interrupt after each
* valid one. Please read section 10.6.2 of ahci 1.1 for more
* information.
*
* Also, use the unmasked value to clear interrupt as spurious
* pending event on a dummy port might cause screaming IRQ.
*/
writel(irq_stat, mmio + HOST_IRQ_STAT);
spin_unlock(&host->lock);
VPRINTK("EXIT\n");
return IRQ_RETVAL(handled);
}
unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_port_priv *pp = ap->private_data;
// elphel test: set tag = 0
//unsigned int tag = 0;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_qc_issue(): port %u queued command issue", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
/* Keep track of the currently active link. It will be used
* in completion path to determine whether NCQ phase is in
* progress.
*/
pp->active_link = qc->dev->link;
if (qc->tf.protocol == ATA_PROT_NCQ) {
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite port %u register PxSACT, value: 0x%08x", ap->port_no, (1 << qc->tag));
//len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite port %u register PxSACT, value: 0x%08x", ap->port_no, (1 << tag));
libahci_debug_event(ap, msg_str, len);
}
writel(1 << qc->tag, port_mmio + PORT_SCR_ACT);
//writel(1 << tag, port_mmio + PORT_SCR_ACT);
}
if (pp->fbs_enabled && pp->fbs_last_dev != qc->dev->link->pmp) {
u32 fbs = readl(port_mmio + PORT_FBS);
fbs &= ~(PORT_FBS_DEV_MASK | PORT_FBS_DEC);
fbs |= qc->dev->link->pmp << PORT_FBS_DEV_OFFSET;
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite port %u register PxFBS, value: 0x%08x", ap->port_no, fbs);
libahci_debug_event(ap, msg_str, len);
}
writel(fbs, port_mmio + PORT_FBS);
pp->fbs_last_dev = qc->dev->link->pmp;
}
writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE);
//writel(1 << tag, port_mmio + PORT_CMD_ISSUE);
ahci_sw_activity(qc->dev->link);
kfree(msg_str);
return 0;
}
EXPORT_SYMBOL_GPL(ahci_qc_issue);
static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
{
struct ahci_port_priv *pp = qc->ap->private_data;
u8 *rx_fis = pp->rx_fis;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_qc_fill_rtf(): read port %u Received FIS and copy to Task File", qc->ap->port_no);
libahci_debug_event(qc->ap, msg_str, len);
kfree(msg_str);
}
if (pp->fbs_enabled)
rx_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ;
/*
* After a successful execution of an ATA PIO data-in command,
* the device doesn't send D2H Reg FIS to update the TF and
* the host should take TF and E_Status from the preceding PIO
* Setup FIS.
*/
if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE &&
!(qc->flags & ATA_QCFLAG_FAILED)) {
ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf);
qc->result_tf.command = (rx_fis + RX_FIS_PIO_SETUP)[15];
libahci_debug_dump_region(qc->ap, (const u32 *)(rx_fis + RX_FIS_PIO_SETUP), 5, "\tread PIO Setup FIS; dump: ");
libahci_debug_dump_sg(qc, "reading data pointed by S/G list; dump: ");
} else {
ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
libahci_debug_dump_region(qc->ap, (const u32 *)(rx_fis + RX_FIS_D2H_REG), 5, "\tread D2H Register FIS; dump: ");
}
return true;
}
static void ahci_freeze(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_freeze(): freeze port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
}
dev_info(ap->host->dev, "ahci_freeze()\n");
/* turn IRQ off */
writel(0, port_mmio + PORT_IRQ_MASK);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "\twrite to port %u register PxIE, value: 0x0", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
}
static void ahci_thaw(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *mmio = hpriv->mmio;
void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
struct ahci_port_priv *pp = ap->private_data;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_thaw(): thaw port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* clear IRQ */
tmp = readl(port_mmio + PORT_IRQ_STAT);
writel(tmp, port_mmio + PORT_IRQ_STAT);
writel(1 << ap->port_no, mmio + HOST_IRQ_STAT);
/* turn IRQ back on */
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
void ahci_error_handler(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_error_handler(): port %u error handler", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
/* restart engine */
ahci_stop_engine(ap);
hpriv->start_engine(ap);
}
sata_pmp_error_handler(ap);
if (!ata_dev_enabled(ap->link.device))
ahci_stop_engine(ap);
}
EXPORT_SYMBOL_GPL(ahci_error_handler);
static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_post_internal_cmd(): port %u post internal command", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* make DMA engine forget about the failed command */
if (qc->flags & ATA_QCFLAG_FAILED)
ahci_kick_engine(ap);
}
static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
struct ata_device *dev = ap->link.device;
u32 devslp, dm, dito, mdat, deto;
int rc;
unsigned int err_mask;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_set_aggressive_devslp(): set aggressive port %u device sleep", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
devslp = readl(port_mmio + PORT_DEVSLP);
if (!(devslp & PORT_DEVSLP_DSP)) {
dev_info(ap->host->dev, "port does not support device sleep\n");
return;
}
/* disable device sleep */
if (!sleep) {
if (devslp & PORT_DEVSLP_ADSE) {
writel(devslp & ~PORT_DEVSLP_ADSE,
port_mmio + PORT_DEVSLP);
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_DISABLE,
SATA_DEVSLP);
if (err_mask && err_mask != AC_ERR_DEV)
ata_dev_warn(dev, "failed to disable DEVSLP\n");
}
return;
}
/* device sleep was already enabled */
if (devslp & PORT_DEVSLP_ADSE)
return;
/* set DITO, MDAT, DETO and enable DevSlp, need to stop engine first */
rc = ahci_stop_engine(ap);
if (rc)
return;
dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET;
dito = devslp_idle_timeout / (dm + 1);
if (dito > 0x3ff)
dito = 0x3ff;
/* Use the nominal value 10 ms if the read MDAT is zero,
* the nominal value of DETO is 20 ms.
*/
if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] &
ATA_LOG_DEVSLP_VALID_MASK) {
mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] &
ATA_LOG_DEVSLP_MDAT_MASK;
if (!mdat)
mdat = 10;
deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO];
if (!deto)
deto = 20;
} else {
mdat = 10;
deto = 20;
}
devslp |= ((dito << PORT_DEVSLP_DITO_OFFSET) |
(mdat << PORT_DEVSLP_MDAT_OFFSET) |
(deto << PORT_DEVSLP_DETO_OFFSET) |
PORT_DEVSLP_ADSE);
writel(devslp, port_mmio + PORT_DEVSLP);
hpriv->start_engine(ap);
/* enable device sleep feature for the drive */
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_ENABLE,
SATA_DEVSLP);
if (err_mask && err_mask != AC_ERR_DEV)
ata_dev_warn(dev, "failed to enable DEVSLP\n");
}
static void ahci_enable_fbs(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 fbs;
int rc;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL && pp->fbs_supported) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_enable_fbs(): enable port %u FIS-based switching", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
if (!pp->fbs_supported)
return;
fbs = readl(port_mmio + PORT_FBS);
if (fbs & PORT_FBS_EN) {
pp->fbs_enabled = true;
pp->fbs_last_dev = -1; /* initialization */
return;
}
rc = ahci_stop_engine(ap);
if (rc)
return;
writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS);
fbs = readl(port_mmio + PORT_FBS);
if (fbs & PORT_FBS_EN) {
dev_info(ap->host->dev, "FBS is enabled\n");
pp->fbs_enabled = true;
pp->fbs_last_dev = -1; /* initialization */
} else
dev_err(ap->host->dev, "Failed to enable FBS\n");
hpriv->start_engine(ap);
}
static void ahci_disable_fbs(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 fbs;
int rc;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL && pp->fbs_supported) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_disable_fbs(): disable port %u FIS-based switching", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
if (!pp->fbs_supported)
return;
fbs = readl(port_mmio + PORT_FBS);
if ((fbs & PORT_FBS_EN) == 0) {
pp->fbs_enabled = false;
return;
}
rc = ahci_stop_engine(ap);
if (rc)
return;
writel(fbs & ~PORT_FBS_EN, port_mmio + PORT_FBS);
fbs = readl(port_mmio + PORT_FBS);
if (fbs & PORT_FBS_EN)
dev_err(ap->host->dev, "Failed to disable FBS\n");
else {
dev_info(ap->host->dev, "FBS is disabled\n");
pp->fbs_enabled = false;
}
hpriv->start_engine(ap);
}
static void ahci_pmp_attach(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_port_priv *pp = ap->private_data;
u32 cmd;
cmd = readl(port_mmio + PORT_CMD);
cmd |= PORT_CMD_PMP;
writel(cmd, port_mmio + PORT_CMD);
ahci_enable_fbs(ap);
pp->intr_mask |= PORT_IRQ_BAD_PMP;
/*
* We must not change the port interrupt mask register if the
* port is marked frozen, the value in pp->intr_mask will be
* restored later when the port is thawed.
*
* Note that during initialization, the port is marked as
* frozen since the irq handler is not yet registered.
*/
if (!(ap->pflags & ATA_PFLAG_FROZEN))
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
static void ahci_pmp_detach(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ahci_port_priv *pp = ap->private_data;
u32 cmd;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
ahci_disable_fbs(ap);
cmd = readl(port_mmio + PORT_CMD);
cmd &= ~PORT_CMD_PMP;
writel(cmd, port_mmio + PORT_CMD);
pp->intr_mask &= ~PORT_IRQ_BAD_PMP;
/* see comment above in ahci_pmp_attach() */
if (!(ap->pflags & ATA_PFLAG_FROZEN))
writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_pmp_detach(), ap->flags=0x%08x PxCMD <= 0x%08x, IE=0x%08x",ap->pflags, cmd, readl(port_mmio + PORT_IRQ_MASK) );
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
}
int ahci_port_resume(struct ata_port *ap)
{
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_port_resume(): first form ahci_elphel.c - never got here? -wrong, got");
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
ahci_power_up(ap);
ahci_start_port(ap);
if (sata_pmp_attached(ap)) // false
ahci_pmp_attach(ap);
else
ahci_pmp_detach(ap);
return 0;
}
EXPORT_SYMBOL_GPL(ahci_port_resume);
#ifdef CONFIG_PM
static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
{
const char *emsg = NULL;
int rc;
rc = ahci_deinit_port(ap, &emsg);
if (rc == 0)
ahci_power_down(ap);
else {
ata_port_err(ap, "%s (%d)\n", emsg, rc);
ata_port_freeze(ap);
}
return rc;
}
#endif
static int ahci_port_start(struct ata_port *ap)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct device *dev = ap->host->dev;
struct ahci_port_priv *pp;
void *mem;
dma_addr_t mem_dma;
size_t dma_sz, rx_fis_sz;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_port_start(): starting port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
if (!pp)
return -ENOMEM;
if (ap->host->n_ports > 1) {
pp->irq_desc = devm_kzalloc(dev, 8, GFP_KERNEL);
if (!pp->irq_desc) {
devm_kfree(dev, pp);
return -ENOMEM;
}
snprintf(pp->irq_desc, 8,
"%s%d", dev_driver_string(dev), ap->port_no);
}
/* check FBS capability */
if ((hpriv->cap & HOST_CAP_FBS) && sata_pmp_supported(ap)) {
void __iomem *port_mmio = ahci_port_base(ap);
u32 cmd = readl(port_mmio + PORT_CMD);
if (cmd & PORT_CMD_FBSCP)
pp->fbs_supported = true;
else if (hpriv->flags & AHCI_HFLAG_YES_FBS) {
dev_info(dev, "port %d can do FBS, forcing FBSCP\n",
ap->port_no);
pp->fbs_supported = true;
} else
dev_warn(dev, "port %d is not capable of FBS\n",
ap->port_no);
}
if (pp->fbs_supported) {
dma_sz = AHCI_PORT_PRIV_FBS_DMA_SZ;
rx_fis_sz = AHCI_RX_FIS_SZ * 16;
} else {
dma_sz = AHCI_PORT_PRIV_DMA_SZ;
rx_fis_sz = AHCI_RX_FIS_SZ;
}
mem = dmam_alloc_coherent(dev, dma_sz, &mem_dma, GFP_KERNEL);
if (!mem)
return -ENOMEM;
memset(mem, 0, dma_sz);
/*
* First item in chunk of DMA memory: 32-slot command table,
* 32 bytes each in size
*/
pp->cmd_slot = mem;
pp->cmd_slot_dma = mem_dma;
mem += AHCI_CMD_SLOT_SZ;
mem_dma += AHCI_CMD_SLOT_SZ;
/*
* Second item: Received-FIS area
*/
pp->rx_fis = mem;
pp->rx_fis_dma = mem_dma;
mem += rx_fis_sz;
mem_dma += rx_fis_sz;
/*
* Third item: data area for storing a single command
* and its scatter-gather table
*/
pp->cmd_tbl = mem;
pp->cmd_tbl_dma = mem_dma;
/*
* Save off initial list of interrupts to be enabled.
* This could be changed later
*/
pp->intr_mask = DEF_PORT_IRQ;
/*
* Switch to per-port locking in case each port has its own MSI vector.
*/
if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) {
spin_lock_init(&pp->lock);
ap->lock = &pp->lock;
}
ap->private_data = pp;
/* engage engines, captain */
return ahci_port_resume(ap);
}
static void ahci_port_stop(struct ata_port *ap)
{
const char *emsg = NULL;
int rc;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_port_stop(): stopping port %u", ap->port_no);
libahci_debug_event(ap, msg_str, len);
kfree(msg_str);
}
/* de-initialize port */
rc = ahci_deinit_port(ap, &emsg);
if (rc)
ata_port_warn(ap, "%s (%d)\n", emsg, rc);
}
void ahci_print_info(struct ata_host *host, const char *scc_s)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->mmio;
u32 vers, cap, cap2, impl, speed;
const char *speed_s;
vers = readl(mmio + HOST_VERSION);
cap = hpriv->cap;
cap2 = hpriv->cap2;
impl = hpriv->port_map;
speed = (cap >> 20) & 0xf;
if (speed == 1)
speed_s = "1.5";
else if (speed == 2)
speed_s = "3";
else if (speed == 3)
speed_s = "6";
else
speed_s = "?";
dev_info(host->dev,
"AHCI %02x%02x.%02x%02x "
"%u slots %u ports %s Gbps 0x%x impl %s mode\n"
,
(vers >> 24) & 0xff,
(vers >> 16) & 0xff,
(vers >> 8) & 0xff,
vers & 0xff,
((cap >> 8) & 0x1f) + 1,
(cap & 0x1f) + 1,
speed_s,
impl,
scc_s);
dev_info(host->dev,
"flags: "
"%s%s%s%s%s%s%s"
"%s%s%s%s%s%s%s"
"%s%s%s%s%s%s%s"
"%s%s\n"
,
cap & HOST_CAP_64 ? "64bit " : "",
cap & HOST_CAP_NCQ ? "ncq " : "",
cap & HOST_CAP_SNTF ? "sntf " : "",
cap & HOST_CAP_MPS ? "ilck " : "",
cap & HOST_CAP_SSS ? "stag " : "",
cap & HOST_CAP_ALPM ? "pm " : "",
cap & HOST_CAP_LED ? "led " : "",
cap & HOST_CAP_CLO ? "clo " : "",
cap & HOST_CAP_ONLY ? "only " : "",
cap & HOST_CAP_PMP ? "pmp " : "",
cap & HOST_CAP_FBS ? "fbs " : "",
cap & HOST_CAP_PIO_MULTI ? "pio " : "",
cap & HOST_CAP_SSC ? "slum " : "",
cap & HOST_CAP_PART ? "part " : "",
cap & HOST_CAP_CCC ? "ccc " : "",
cap & HOST_CAP_EMS ? "ems " : "",
cap & HOST_CAP_SXS ? "sxs " : "",
cap2 & HOST_CAP2_DESO ? "deso " : "",
cap2 & HOST_CAP2_SADM ? "sadm " : "",
cap2 & HOST_CAP2_SDS ? "sds " : "",
cap2 & HOST_CAP2_APST ? "apst " : "",
cap2 & HOST_CAP2_NVMHCI ? "nvmp " : "",
cap2 & HOST_CAP2_BOH ? "boh " : ""
);
}
EXPORT_SYMBOL_GPL(ahci_print_info);
void ahci_set_em_messages(struct ahci_host_priv *hpriv,
struct ata_port_info *pi)
{
u8 messages;
void __iomem *mmio = hpriv->mmio;
u32 em_loc = readl(mmio + HOST_EM_LOC);
u32 em_ctl = readl(mmio + HOST_EM_CTL);
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_set_em_messages(): store EM registers");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
if (!ahci_em_messages || !(hpriv->cap & HOST_CAP_EMS))
return;
messages = (em_ctl & EM_CTRL_MSG_TYPE) >> 16;
if (messages) {
/* store em_loc */
hpriv->em_loc = ((em_loc >> 16) * 4);
hpriv->em_buf_sz = ((em_loc & 0xff) * 4);
hpriv->em_msg_type = messages;
pi->flags |= ATA_FLAG_EM;
if (!(em_ctl & EM_CTL_ALHD))
pi->flags |= ATA_FLAG_SW_ACTIVITY;
}
}
EXPORT_SYMBOL_GPL(ahci_set_em_messages);
static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
struct scsi_host_template *sht)
{
int i, rc;
rc = ata_host_start(host);
if (rc)
return rc;
for (i = 0; i < host->n_ports; i++) {
struct ahci_port_priv *pp = host->ports[i]->private_data;
/* Do not receive interrupts sent by dummy ports */
if (!pp) {
disable_irq(irq + i);
continue;
}
rc = devm_request_threaded_irq(host->dev, irq + i,
ahci_multi_irqs_intr,
ahci_port_thread_fn, IRQF_SHARED,
pp->irq_desc, host->ports[i]);
if (rc)
goto out_free_irqs;
}
for (i = 0; i < host->n_ports; i++)
ata_port_desc(host->ports[i], "irq %d", irq + i);
rc = ata_host_register(host, sht);
if (rc)
goto out_free_all_irqs;
return 0;
out_free_all_irqs:
i = host->n_ports;
out_free_irqs:
for (i--; i >= 0; i--)
devm_free_irq(host->dev, irq + i, host->ports[i]);
return rc;
}
/**
* ahci_host_activate - start AHCI host, request IRQs and register it
* @host: target ATA host
* @irq: base IRQ number to request
* @sht: scsi_host_template to use when registering the host
*
* Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
* when multiple MSIs were allocated. That is one MSI per port, starting
* from @irq.
*
* LOCKING:
* Inherited from calling layer (may sleep).
*
* RETURNS:
* 0 on success, -errno otherwise.
*/
int ahci_host_activate(struct ata_host *host, int irq,
struct scsi_host_template *sht)
{
struct ahci_host_priv *hpriv = host->private_data;
int rc;
int len;
char *msg_str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (msg_str != NULL) {
len = snprintf(msg_str, LIBAHCI_DEBUG_BUFSZ, "ahci_host_activate(): activate AHCI host");
libahci_debug_event(NULL, msg_str, len);
kfree(msg_str);
}
if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
rc = ahci_host_activate_multi_irqs(host, irq, sht);
else
rc = ata_host_activate(host, irq, ahci_single_irq_intr,
IRQF_SHARED, sht);
return rc;
}
EXPORT_SYMBOL_GPL(ahci_host_activate);
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("Common AHCI SATA low-level routines");
MODULE_LICENSE("GPL");
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/ata/libahci_debug.c 0000664 0000000 0000000 00000070134 12666203034 0027123 0 ustar 00root root 0000000 0000000 /*
* libahci_debug.c
*
* Created on: Jan 20, 2016
* Author: mk
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "libahci_debug.h"
#include // ioremap(), copy_fromio
#include
/*
struct elphel_buf_t
{
void *vaddr;
dma_addr_t paddr;
ssize_t size;
memcpy(to,from,sizeof())
};
extern struct elphel_buf_t *pElphel_buf;
*/
static u32 page_cntr = 0;
static struct dentry *debug_root = NULL;
static struct libahci_debug_list debug_list = {.debug = 0};
static struct ahci_cmd cmd;
static bool load_flag = false;
const size_t maxigp1_start = 0x80000000; // start of MAXIGP1 physical address range
const size_t maxigp1_size = 0x3000; // size of register memory to save
const size_t buffer_offset = 0x40000; // start of dumping area (0xxxx, 1xxxx and 2xxxx are used for dma buffers
const size_t counter_offset = 0x3fff8; // save page counter and page size with this offset in the buffer
const size_t fsm_state_offset = 0xffc;
static void * ioptr = 0; // keep iomemory mapped forever
char *early_buff;
static inline u32 libahci_debug_get_fsm_state(void);
//static struct mem_buffer mem_buff;
/*
* Print PxIS (0x10) analysis
*/
void libahci_debug_dump_irq(u32 status)
{
int len = 0;
int pos;
char *str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (!str)
return;
len = snprintf(str, LIBAHCI_DEBUG_BUFSZ, "\tinterrupt analysis: ");
pos = len;
if (status & PORT_IRQ_D2H_REG_FIS) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "D2H Register FIS * ");
pos += len;
}
if (status & PORT_IRQ_PIOS_FIS) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, " PIO Setup FIS * ");
pos += len;
}
if (status & PORT_IRQ_DMAS_FIS) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "DMA Setup FIS * ");
pos += len;
}
if (status & PORT_IRQ_SDB_FIS) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Set Device Bits FIS * ");
pos += len;
}
if (status & PORT_IRQ_UNK_FIS) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Unknown FIS * ");
pos += len;
}
if (status & PORT_IRQ_SG_DONE) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Descriptor processed * ");
pos += len;
}
if (status & PORT_IRQ_CONNECT) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Port connect change status * ");
pos += len;
}
if (status & PORT_IRQ_DEV_ILCK) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Device interlock * ");
pos += len;
}
if (status & PORT_IRQ_PHYRDY) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "PhyRdy change status * ");
pos += len;
}
if (status & PORT_IRQ_BAD_PMP) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Incorrect port multiplier * ");
pos += len;
}
if (status & PORT_IRQ_OVERFLOW) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Overflow * ");
pos += len;
}
if (status & PORT_IRQ_IF_NONFATAL) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Iface nonfatal error * ");
pos += len;
}
if (status & PORT_IRQ_IF_ERR) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Iface fatal error * ");
pos += len;
}
if (status & PORT_IRQ_HBUS_DATA_ERR) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Host bus data error * ");
pos += len;
}
if (status & PORT_IRQ_HBUS_ERR) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Host bus fatal error * ");
pos += len;
}
if (status & PORT_IRQ_TF_ERR) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Task file error * ");
pos += len;
}
if (status & PORT_IRQ_COLD_PRES) {
len = snprintf(&str[pos], LIBAHCI_DEBUG_BUFSZ - pos, "Cold port detect * ");
pos += len;
}
libahci_debug_event(NULL, str, pos);
kfree(str);
}
EXPORT_SYMBOL_GPL(libahci_debug_dump_irq);
/*
* Read memory region pointed to by buff and dump its content
*/
void libahci_debug_dump_region(const struct ata_port *ap, const u32 *buff, size_t buff_sz, const char *prefix)
{
int i;
int len, sz;
char *str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (!str)
return;
len = strlen(prefix);
if (len < LIBAHCI_DEBUG_BUFSZ) {
strncpy(str, prefix, len);
} else {
len = 0;
}
for (i = 0; i < buff_sz; i++) {
sz = snprintf(&str[len], LIBAHCI_DEBUG_BUFSZ - len, "0x%08x ", buff[i]);
len += sz;
}
libahci_debug_event(ap, str, len);
kfree(str);
}
EXPORT_SYMBOL_GPL(libahci_debug_dump_region);
/*
* Copy data from S/G list to linear buffer and dump the data
*/
void libahci_debug_dump_sg(const struct ata_queued_cmd *qc, const char *prefix)
{
struct scatterlist *sg;
int si;
int i;
int len;
int sz;
int line_brk;
u32 buff_sz = 0;
u32 buff_ptr = 0;
char *buff;
char *str;
u32 *buff_map;
// Calculate the amount of memory needed
for_each_sg(qc->sg, sg, qc->n_elem, si) {
buff_sz += sg_dma_len(sg);
}
buff = kzalloc(buff_sz, GFP_KERNEL);
if (!buff) {
return;
}
str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (!str) {
kfree(buff);
return;
}
// Copy data from all DMA buffers
dma_sync_sg_for_cpu(&qc->dev->tdev, qc->sg, qc->n_elem, qc->dma_dir);
for_each_sg(qc->sg, sg, qc->n_elem, si) {
u32 sg_len = sg_dma_len(sg);
sz = sg_copy_to_buffer(sg, 1, buff + buff_ptr, sg_len);
buff_ptr += sz;
}
dma_sync_sg_for_device(&qc->dev->tdev, qc->sg, qc->n_elem, qc->dma_dir);
// Print the content of DMA buffers
buff_map = (u32 *)buff;
len = snprintf(str, LIBAHCI_DEBUG_BUFSZ, "\t%s\t%u bytes\n\t", prefix, buff_ptr);
for (i = 0, line_brk = 0; i < buff_ptr / 4; i++) {
sz = snprintf(&str[len], LIBAHCI_DEBUG_BUFSZ - len, "0x%08x ", buff_map[i]);
len += sz;
line_brk++;
if (line_brk >= 8) {
libahci_debug_event(qc->ap, str, len);
line_brk = 0;
len = snprintf(str, LIBAHCI_DEBUG_BUFSZ, "\t");
}
}
if (line_brk != 0) {
libahci_debug_event(qc->ap, str, len);
}
//printk(KERN_DEBUG "%s\tdump S/G list", MARKER);
kfree(buff);
kfree(str);
}
EXPORT_SYMBOL_GPL(libahci_debug_dump_sg);
static void libahci_debug_read_host_regs(struct ata_host *host, struct host_regs *regs)
{
struct ahci_host_priv *hpriv = host->private_data;
void __iomem *host_mmio = hpriv->mmio;
regs->CAP = readl(host_mmio + HOST_CAP);
regs->CAP2 = readl(host_mmio + HOST_CAP2);
regs->GHC = readl(host_mmio + HOST_CTL);
regs->IS = readl(host_mmio + HOST_IRQ_STAT);
regs->PI = readl(host_mmio + HOST_PORTS_IMPL);
regs->VS = readl(host_mmio + HOST_VERSION);
regs->CCC_CTL = readl(host_mmio + 0x14);
regs->CCC_PORTS = readl(host_mmio + 0x18);
regs->EM_CTL = readl(host_mmio + HOST_EM_CTL);
regs->EM_LOC = readl(host_mmio + HOST_EM_LOC);
regs->BOHC = readl(host_mmio + 0x28);
}
static void libahci_debug_read_port_regs(struct ata_port *ap, struct port_regs *pr)
{
void __iomem *port_mmio = ahci_port_base(ap);
int i;
pr->PxCLB = readl(port_mmio + PORT_LST_ADDR);
pr->PxCLBU = readl(port_mmio + PORT_LST_ADDR_HI);
pr->PxFB = readl(port_mmio + PORT_FIS_ADDR);
pr->PxFBU = readl(port_mmio + PORT_FIS_ADDR_HI);
pr->PxIS = readl(port_mmio + PORT_IRQ_STAT);
pr->PxIE = readl(port_mmio + PORT_IRQ_MASK);
pr->PxCMD = readl(port_mmio + PORT_CMD);
//pr->reserved_1 = readl(port_mmio + PORT_RESERVED_1);
pr->PxTFD = readl(port_mmio + PORT_TFDATA);
pr->PxSIG = readl(port_mmio + PORT_SIG);
pr->PxSSTS = readl(port_mmio + PORT_SCR_STAT);
pr->PxSCTL = readl(port_mmio + PORT_SCR_CTL);
pr->PxSERR = readl(port_mmio + PORT_SCR_ERR);
pr->PxSACT = readl(port_mmio + PORT_SCR_ACT);
pr->PxCI = readl(port_mmio + PORT_CMD_ISSUE);
pr->PxSNTF = readl(port_mmio + PORT_SCR_NTF);
pr->PxFBS = readl(port_mmio + PORT_FBS);
pr->PxDEVSLP = readl(port_mmio + PORT_DEVSLP);
for (i = 0; i < PORT_VENDOR_BYTES; i++) {
pr->reserved_2[i] = readb(port_mmio + 0x70 + i);
}
}
static int libahci_debug_host_show(struct seq_file *f, void *p)
{
struct ata_host *host = f->private;
struct host_regs hr = {0};
libahci_debug_read_host_regs(host, &hr);
seq_printf(f, "CAP:\t\t0x%08X\n", hr.CAP);
seq_printf(f, "CAP2:\t\t0x%08X\n", hr.CAP2);
seq_printf(f, "GHC:\t\t0x%08X\n", hr.GHC);
seq_printf(f, "IS:\t\t0x%08X\n", hr.IS);
seq_printf(f, "PI:\t\t0x%08X\n", hr.PI);
seq_printf(f, "VS:\t\t0x%08X\n", hr.VS);
seq_printf(f, "CCC_CTL:\t0x%08X\n", hr.CCC_CTL);
seq_printf(f, "CCC_PORTS:\t0x%08X\n", hr.CCC_PORTS);
seq_printf(f, "EM_LOC:\t\t0x%08X\n", hr.EM_LOC);
seq_printf(f, "EM_CTL:\t\t0x%08X\n", hr.EM_CTL);
seq_printf(f, "BOHC:\t\t0x%08X\n", hr.BOHC);
seq_printf(f, "\nbuffer location:\t\t0x%08X\n", pElphel_buf->paddr);
return 0;
}
static int libahci_debug_rdesc_show(struct seq_file *f, void *p)
{
struct ata_port *ap = f->private;
struct port_regs pr = {0};
int i;
libahci_debug_read_port_regs(ap, &pr);
seq_printf(f, "PxCLB:\t\t0x%08X\n", pr.PxCLB);
seq_printf(f, "PxCLBU:\t\t0x%08X\n", pr.PxCLBU);
seq_printf(f, "PxFB:\t\tx0%08X\n", pr.PxFB);
seq_printf(f, "PxFBU:\t\t0x%08X\n", pr.PxFBU);
seq_printf(f, "PxIS:\t\t0x%08X\n", pr.PxIS);
seq_printf(f, "PxIE:\t\t0x%08X\n", pr.PxIE);
seq_printf(f, "PxCMD:\t\t0x%08X\n", pr.PxCMD);
seq_printf(f, "reserved:\t0x%08X\n", pr.reserved_1);
seq_printf(f, "PxTFD:\t\t0x%08X\n", pr.PxTFD);
seq_printf(f, "PxSIG:\t\t0x%08X\n", pr.PxSIG);
seq_printf(f, "PxSSTS:\t\t0x%08X\n", pr.PxSSTS);
seq_printf(f, "PxSCTL:\t\t0x%08X\n", pr.PxSCTL);
seq_printf(f, "PxSERR:\t\t0x%08X\n", pr.PxSERR);
seq_printf(f, "PxSACT:\t\t0x%08X\n", pr.PxSACT);
seq_printf(f, "PxCI:\t\t0x%08X\n", pr.PxCI);
seq_printf(f, "PxSNTF:\t\t0x%08X\n", pr.PxSNTF);
seq_printf(f, "PxFBS:\t\t0x%08X\n", pr.PxFBS);
seq_printf(f, "PxDEVSLP:\t0x%08X\n", pr.PxDEVSLP);
seq_printf(f, "reserved area:\t");
for (i = 0; i < PORT_RESERVED_2; i++) {
seq_printf(f, "0x%02X ", pr.reserved_2[i]);
}
seq_printf(f, "\nVendor specific bytes:\t");
for (i = 0; i < PORT_VENDOR_BYTES; i++) {
seq_printf(f, "0x%02X ", pr.PxVS[i]);
}
seq_printf(f, "\n");
return 0;
}
static int libahci_debug_host_open(struct inode *i_node, struct file *f)
{
return single_open(f, libahci_debug_host_show, i_node->i_private);
}
static int libahci_debug_rdesc_open(struct inode *i_node, struct file *f)
{
return single_open(f, libahci_debug_rdesc_show, i_node->i_private);
}
static int libahci_debug_events_open(struct inode *i_node, struct file *f)
{
int err = 0;
unsigned long flags;
struct libahci_debug_list *list;
struct ata_port *port = i_node->i_private;
debug_list.debug = 1;
//Create event buffer for current port
list = kzalloc(sizeof(struct libahci_debug_list), GFP_KERNEL);
if (!list) {
err = -ENOMEM;
goto fail_handler;
}
if (!(list->libahci_debug_buf = kzalloc(sizeof(char) * LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL))) {
err = -ENOMEM;
kfree(list);
goto fail_handler;
}
list->port_n = port->port_no;
mutex_init(&list->read_mutex);
init_waitqueue_head(&list->debug_wait);
spin_lock_irqsave(&list->debug_list_lock, flags);
list_add_tail(&list->node, &debug_list.node);
spin_unlock_irqrestore(&list->debug_list_lock, flags);
// Associate debug list entry with corresponding file
f->private_data = list;
fail_handler:
return err;
}
static ssize_t libahci_debug_events_read(struct file *f, char __user *buff, size_t sz, loff_t *pos)
{
int ret = 0, len;
struct libahci_debug_list *list = f->private_data;
DECLARE_WAITQUEUE(wait, current);
mutex_lock(&list->read_mutex);
while (ret == 0) {
if (list->head == list->tail) {
// Buffer is empty, put the queue in sleep mode
add_wait_queue(&list->debug_wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
while (list->head == list->tail) {
if (f->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
mutex_unlock(&list->read_mutex);
schedule();
mutex_lock(&list->read_mutex);
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&list->debug_wait, &wait);
}
copy_rest:
//printk(KERN_DEBUG "%s Read event", MARKER);
if (list->head != list->tail && ret == 0) {
if (list->tail > list->head) {
len = list->tail - list->head;
if (copy_to_user(buff + ret, &list->libahci_debug_buf[list->head], len)) {
ret = -EFAULT;
} else {
ret += len;
list->head += len;
}
} else {
len = LIBAHCI_DEBUG_BUFSZ - list->head;
if (copy_to_user(buff, &list->libahci_debug_buf[list->head], len)) {
ret = -EFAULT;
} else {
ret += len;
list->head = 0;
goto copy_rest;
}
}
}
//printk(KERN_DEBUG "%s\thead now is %u", MARKER, list->head);
}
mutex_unlock(&list->read_mutex);
return ret;
}
static unsigned int libahci_debug_events_poll(struct file *f, struct poll_table_struct *wait)
{
struct libahci_debug_list *list = f->private_data;
poll_wait(f, &list->debug_wait, wait);
if (list->head != list->tail) {
return POLLIN | POLLRDNORM;
}
return 0;
}
static int libahci_debug_events_release(struct inode *i_node, struct file *f)
{
struct libahci_debug_list *list = f->private_data;
unsigned long flags;
debug_list.debug = 0;
spin_lock_irqsave(&list->debug_list_lock, flags);
list_del(&list->node);
spin_unlock_irqrestore(&list->debug_list_lock, flags);
kfree(list->libahci_debug_buf);
kfree(list);
return 0;
}
void libahci_debug_event(const struct ata_port *port, char *msg, size_t msg_sz)
{
int len;
int i;
u32 tmp;
char *format_msg = NULL;
unsigned long flags;
unsigned int port_index = (port == NULL) ? 0 : port->port_no;
struct libahci_debug_list *list = NULL, *pos = NULL;
//printk(KERN_DEBUG "%s Write event: %s", MARKER, msg);
if (debug_list.debug) {
format_msg = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (format_msg != NULL) {
// Find buffer which this event is addressed to
list_for_each_entry(list, &debug_list.node, node) {
if (list->port_n == port_index) {
pos = list;
}
}
if (pos != NULL) {
//i = libahci_debug_state_dump(port);
i = libahci_debug_saxigp1_save(port, 0x3000);
tmp = libahci_debug_get_fsm_state();
len = snprintf(format_msg, LIBAHCI_DEBUG_BUFSZ, "%s [%08u; fsm: 0x%08x @0x%08x] %s\n", EVT_MARKER, i, tmp, jiffies, msg);
spin_lock_irqsave(&pos->debug_list_lock, flags);
for (i = 0; i < len; i++) {
pos->libahci_debug_buf[(pos->tail+ i) % LIBAHCI_DEBUG_BUFSZ] = format_msg[i];
}
pos->tail = (pos->tail + len) % LIBAHCI_DEBUG_BUFSZ;
spin_unlock_irqrestore(&pos->debug_list_lock, flags);
//printk(KERN_DEBUG "%s\ttail is now %u", MARKER, pos->tail);
// Wake up the queue which should be sleeping
wake_up_interruptible(&pos->debug_wait);
}
kfree(format_msg);
}
} else {
if (early_buff) {
}
}
}
EXPORT_SYMBOL_GPL(libahci_debug_event);
static void libahci_debug_prep_cfis(struct ahci_cmd_fis *cmd, u32 *fis, u8 pmp)
{
fis[0] = cmd->dw0;
fis[1] = cmd->dw1;
fis[2] = cmd->dw2;
fis[3] = cmd->dw3;
fis[4] = cmd->dw4;
}
void libahci_debug_exec_cmd(struct ata_port *ap)
{
void *cmd_tbl;
dma_addr_t cmd_tbl_dma;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_sg *ahci_sg;
struct ahci_cmd_hdr *data;
void __iomem *port_mmio = ahci_port_base(ap);
unsigned int slot;
printk(KERN_DEBUG "%s Executing command for port %u", MARKER, ap->port_no);
slot = readl(port_mmio + PORT_CMD_ISSUE);
printk(KERN_DEBUG "%s PxCI: 0x%08x", MARKER, slot);
slot = ffz(slot);
printk(KERN_DEBUG "%s Preparing command; using slot %u", MARKER, slot);
cmd_tbl = pp->cmd_tbl + slot * AHCI_CMD_TBL_SZ;
libahci_debug_prep_cfis(&cmd.fis, cmd_tbl, ap->link.pmp);
libahci_debug_dump_region(ap, (const u32 *)cmd_tbl, 5, "\tcfis data dump: ");
/*printk(KERN_DEBUG "%s\tcfis data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x DW4 = 0x%08x",
MARKER, cmd.fis.dw0, cmd.fis.dw1, cmd.fis.dw2, cmd.fis.dw3, cmd.fis.dw4);*/
printk(KERN_DEBUG "%s Preparing one S/G list", MARKER);
ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ;
ahci_sg->addr = cpu_to_le32(sg_dma_address(&cmd.sg) & 0xffffffff);
ahci_sg->addr_hi = cpu_to_le32((sg_dma_address(&cmd.sg) >> 16) >> 16);
ahci_sg->flags_size = cpu_to_le32(sg_dma_len(&cmd.sg));
printk(KERN_DEBUG "%s Preparing command header", MARKER);
cmd_tbl_dma = pp->cmd_tbl_dma + slot * AHCI_CMD_TBL_SZ;
pp->cmd_slot[slot].opts = cpu_to_le32(cmd.hdr.opts);
pp->cmd_slot[slot].status = 0;
pp->cmd_slot[slot].tbl_addr = cpu_to_le32(cmd_tbl_dma & 0xffffffff);
pp->cmd_slot[slot].tbl_addr_hi = cpu_to_le32((cmd_tbl_dma >> 16) >> 16);
data = &pp->cmd_slot[slot];
printk(KERN_DEBUG "%s\tchdr data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x", MARKER, data->opts,
data->status, data->tbl_addr, data->tbl_addr_hi);
printk(KERN_DEBUG "%s Issuing command", MARKER);
writel(1 << slot, port_mmio + PORT_CMD_ISSUE);
cmd.cmd_sent = true;
}
EXPORT_SYMBOL_GPL(libahci_debug_exec_cmd);
void libahci_debug_irq_notify(const struct ata_port *ap)
{
int i, sz, line_brk, ptr;
size_t len;
char *buff = kzalloc(CMD_DMA_BUFSZ, GFP_KERNEL);
char *str = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
u32 *buff_map;
struct ahci_port_priv *pp = ap->private_data;
u32 *rx_fis = pp->rx_fis;
void __iomem *port_mmio = ahci_port_base((struct ata_port *)ap);
u32 tmp;
if (!buff || !str)
return;
printk(KERN_DEBUG "%s IRQ notify event, flag: %d", MARKER, cmd.cmd_sent);
if (cmd.cmd_sent) {
tmp = readl(port_mmio + PORT_SCR_ERR);
printk(KERN_DEBUG "%s PxSERR: 0x%08x", MARKER, tmp);
// Read RX FIS memory
libahci_debug_dump_region(ap, rx_fis + RX_FIS_PIO_SETUP, 5, "\tread PIO SETUP FIS region; dump: ");
libahci_debug_dump_region(ap, rx_fis + RX_FIS_D2H_REG, 5, "\tread D2H Register FIS; dump: ");
dma_sync_sg_for_cpu(ap->dev, &cmd.sg, 1, DMA_BIDIRECTIONAL);
len = sg_copy_to_buffer(&cmd.sg, 1, buff, CMD_DMA_BUFSZ);
dma_sync_sg_for_device(ap->dev, &cmd.sg, 1, DMA_BIDIRECTIONAL);
printk(KERN_DEBUG "%s %u bytes copied from DMA buffer", MARKER, len);
// Print the content of DMA buffers
buff_map = (u32 *)buff;
for (i = 0, line_brk = 0, ptr = 0; i < len / 4; i++) {
sz = snprintf(&str[ptr], LIBAHCI_DEBUG_BUFSZ - ptr, "0x%08x ", buff_map[i]);
ptr += sz;
line_brk++;
if (line_brk >= 8) {
libahci_debug_event(ap, str, ptr);
line_brk = 0;
ptr = snprintf(str, LIBAHCI_DEBUG_BUFSZ, "\t");
}
}
if (line_brk != 0) {
libahci_debug_event(ap, str, ptr);
}
// Command processed, clear flag
cmd.cmd_sent = false;
}
}
EXPORT_SYMBOL_GPL(libahci_debug_irq_notify);
static int libahci_debug_fis_open(struct inode *i_node, struct file *f)
{
struct ata_port *ap = i_node->i_private;
// const char *name = f->f_path.dentry->d_name.name;
// void *buff = NULL;
/*if (strncmp(name, FILE_NAME_CFIS, 5) == 0) {
buff = kzalloc(sizeof(struct ahci_cmd_fis), GFP_KERNEL);
} else if (strncmp(name, FILE_NAME_CHDR, 5) == 0) {
buff = kzalloc(sizeof(struct ahci_cmd_hdr), GFP_KERNEL);
}
if (!buff)
return -ENOMEM;*/
f->private_data = ap;
return 0;
}
static ssize_t libahci_debug_cfis_write(struct file *f, const char __user *buff, size_t buff_sz, loff_t *ppos)
{
struct ahci_cmd_fis *data = &cmd.fis;
if (buff_sz != CMD_FIS_SZ) {
return -EINVAL;
}
data->dw0 = (((buff[3] & 0xff) << 24) | ((buff[2] & 0xff) << 16) | ((buff[1] & 0xff) << 8) | (buff[0] & 0xff));
data->dw1 = (((buff[7] & 0xff) << 24) | ((buff[6] & 0xff) << 16) | ((buff[5] & 0xff) << 8) | (buff[4] & 0xff));
data->dw2 = (((buff[11] & 0xff) << 24) | ((buff[10] & 0xff) << 16) | ((buff[9] & 0xff) << 8) | (buff[8] & 0xff));
data->dw3 = (((buff[15] & 0xff) << 24) | ((buff[14] & 0xff) << 16) | ((buff[13] & 0xff) << 8) | (buff[12] & 0xff));
data->dw4 = (((buff[19] & 0xff) << 24) | ((buff[18] & 0xff) << 16) | ((buff[17] & 0xff) << 8) | (buff[16] & 0xff));
//printk(KERN_DEBUG "%s cfis data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x DW4 = 0x%08x, pos = %lld",
// MARKER, data->dw0, data->dw1, data->dw2, data->dw3, data->dw4, *ppos);
return buff_sz;
}
static ssize_t libahci_debug_chdr_write(struct file *f, const char __user *buff, size_t buff_sz, loff_t *ppos)
{
struct ahci_cmd_hdr *data = &cmd.hdr;
if (buff_sz != CMD_HDR_SZ) {
return -EINVAL;
}
data->opts = (((buff[3] & 0xff) << 24) | ((buff[2] & 0xff) << 16) | ((buff[1] & 0xff) << 8) | (buff[0] & 0xff));
data->status = (((buff[7] & 0xff) << 24) | ((buff[6] & 0xff) << 16) | ((buff[5] & 0xff) << 8) | (buff[4] & 0xff));
data->tbl_addr = (((buff[11] & 0xff) << 24) | ((buff[10] & 0xff) << 16) | ((buff[9] & 0xff) << 8) | (buff[8] & 0xff));
data->tbl_addr_hi = (((buff[15] & 0xff) << 24) | ((buff[14] & 0xff) << 16) | ((buff[13] & 0xff) << 8) | (buff[12] & 0xff));
//printk(KERN_DEBUG "%s chdr data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x, pos = %lld", MARKER, data->opts,
// data->status, data->tbl_addr, data->tbl_addr_hi, *ppos);
libahci_debug_exec_cmd(f->private_data);
return buff_sz;
}
static int libahci_debug_fis_release(struct inode *i_node, struct file *f)
{
return 0;
}
static ssize_t libahci_debug_load(struct file *f, const char __user *buff, size_t buff_sz, loff_t *ppos)
{
load_flag = true;
return buff_sz;
}
/*
* This function waits until the loading flag is set through debugfs file.
* The state of the flag is checked every 100ms.
*/
void libahci_debug_wait_flag(void)
{
char *msg_str = "waiting for flag to be written to debugfs";
printk(KERN_DEBUG "%s Waiting for flag to be written to debugfs", MARKER);
libahci_debug_event(NULL, msg_str, sizeof(*msg_str));
while (load_flag == false) {
/*set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(msecs_to_jiffies(100));*/
msleep(500);
}
load_flag = false;
}
EXPORT_SYMBOL_GPL(libahci_debug_wait_flag);
static void libahci_debug_buff_line(void *mem, u32 cntr)
{
int i;
u32 *mem_ptr = mem;
mem_ptr[0] = cntr;
for (i = 1; i < MARKER_LEN; i++) {
mem_ptr[i] = 0xa5a5a5a5;
}
}
/*
* Copy controller registers to buffer memory and return the record number
*/
unsigned int libahci_debug_state_dump(struct ata_port *ap)
{
// static u32 page_cntr;
int i;
u32 tmp;
u32 ptr;
u32 * buf = (u32 *) (pElphel_buf->vaddr);
struct device *dev = ap->dev;
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *host_mmio = hpriv->mmio;
struct ahci_port_priv *ppriv = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
if (!ap)
return 0;
if (!pElphel_buf->vaddr) {
dev_err(dev, "dump buffer has not been allocated");
return 0;
}
dev_info(dev, "dump page num: %u", page_cntr);
ptr = page_cntr * DUMP_LEN;
if (ptr + DUMP_LEN > SEGMENT_SIZE)
ptr = 0;
dev_info(dev, "current ptr: %u", ptr);
for (i = 0; i < GHC_SZ; i++) {
tmp = ioread32(host_mmio + 4 * i);
buf[ptr++] = tmp;
}
for (i = 0; i < PORT_REG_SZ; i++) {
tmp = ioread32(port_mmio + 4 * i);
buf[ptr++] = tmp;
}
for (i = 0; i < CLB_SZ; i++) {
tmp = ioread32(ppriv->cmd_slot);
buf[ptr++] = tmp;
}
for (i = 0; i < FIS_SZ; i++) {
tmp = ioread32(ppriv->rx_fis);
buf[ptr++] = tmp;
}
libahci_debug_buff_line(pElphel_buf->vaddr +ptr + ALIGN_OFFSET, page_cntr);
//__cpuc_flush_kern_all();
//outer_flush_all();
page_cntr++;
return page_cntr;
}
EXPORT_SYMBOL_GPL(libahci_debug_state_dump);
unsigned int libahci_debug_saxigp1_save(struct ata_port *ap, size_t dump_size)
{
struct device *dev;
u32 * counter_save;
void *current_ptr;
static size_t bytes_copied;
const size_t end = 0x1000000;
u32 *start_ptr;
// ap pointer is not initialized on early loading stages, skip debug output
if (ap) {
dev = ap->dev;
if (!ioptr) {
dev_err(dev, "saxigp1 memory is not mapped");
return 0; // should be non-zero when error, 0 is OK usually
}
if (!pElphel_buf->vaddr) {
dev_err(dev, "elphel_buf has not been allocated");
return 0; // should be non-zero when error, 0 is OK usually
}
}
//if (bytes_copied < pElphel_buf->size * PAGE_SIZE - dump_size) {
if (bytes_copied < end) {
counter_save = (u32*) (pElphel_buf->vaddr + counter_offset);
start_ptr = (u32 *)(pElphel_buf->vaddr + buffer_offset + (page_cntr * dump_size));
//dev_err(dev, "Copying 0x%x bytes of data from saxigp1 to memory",dump_size);
memcpy_fromio(pElphel_buf->vaddr + buffer_offset + (page_cntr * dump_size), ioptr, dump_size);
counter_save[0] = page_cntr;
counter_save[1] = dump_size;
page_cntr++;
bytes_copied += dump_size;
} else {
page_cntr = 0;
}
return page_cntr;
}
EXPORT_SYMBOL_GPL(libahci_debug_saxigp1_save);
static void libahci_debug_buff_init(struct device *dev)
{
dev_info(dev, "Nothing to allocate - using elphel_buf allocated at startup");
/*
mem_buff.vaddr = dmam_alloc_coherent(dev, SEGMENT_SIZE, &mem_buff.paddr, GFP_KERNEL);
if (!mem_buff.vaddr)
dev_err(dev, "unable to allocate memory");
else
dev_info(dev, "dump buffer allocated");
*/
/*
const size_t maxigp1_start = 0x80000000; // start of MAXIGP1 physical address range
const size_t maxigp1_size = 0x3000; // size of register memory to save
*/
ioptr = ioremap_nocache(maxigp1_start, maxigp1_size);
dev_info(dev, "Mapped 0x%08x bytes from physical address 0x%08x to 0x%08x", maxigp1_size, maxigp1_start, (size_t) ioptr);
page_cntr = 0;
}
static inline u32 libahci_debug_get_fsm_state(void)
{
return ioread32(ioptr + fsm_state_offset);
}
static const struct file_operations libahci_debug_host_ops = {
.open = libahci_debug_host_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations libahci_debug_rdesc_ops = {
.open = libahci_debug_rdesc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations libahci_debug_events_fops = {
.owner = THIS_MODULE,
.open = libahci_debug_events_open,
.read = libahci_debug_events_read,
.poll = libahci_debug_events_poll,
.release = libahci_debug_events_release,
.llseek = noop_llseek,
};
static const struct file_operations libahci_debug_cfis_ops = {
.open = libahci_debug_fis_open,
.write = libahci_debug_cfis_write,
.release = libahci_debug_fis_release,
};
static const struct file_operations libahci_debug_chdr_ops = {
.open = libahci_debug_fis_open,
.write = libahci_debug_chdr_write,
.release = libahci_debug_fis_release,
};
static const struct file_operations libahci_debug_load_ops= {
.write = libahci_debug_load,
};
static int libahci_debug_init_sg(void)
{
cmd.sg_buff = kzalloc(CMD_DMA_BUFSZ, GFP_KERNEL);
// mark the area
memset(cmd.sg_buff, 0xa5, CMD_DMA_BUFSZ);
if (!cmd.sg_buff) {
return -ENOMEM;
} else {
sg_init_one(&cmd.sg, cmd.sg_buff, CMD_DMA_BUFSZ);
}
return 0;
}
int libahci_debug_init_early(struct device *dev)
{
early_buff = kzalloc(LIBAHCI_DEBUG_BUFSZ, GFP_KERNEL);
if (!early_buff) {
dev_err(dev, "unable to allocate mem for early buffer");
return -ENOMEM;
} else {
dev_info(dev, "early buffer allocated");
}
}
int libahci_debug_init(struct ata_host *host)
{
int i;
char port_n[] = "port00";
struct dentry *node;
INIT_LIST_HEAD(&debug_list.node);
printk(KERN_DEBUG "%s Loading debug AHCI driver", MARKER);
debug_root = debugfs_create_dir(ROOT_DIR_NAME, NULL);
if (!debug_root) {
goto fail_handler;
}
debugfs_create_file("rdesc_host", 0644,
debug_root, host, &libahci_debug_host_ops);
debugfs_create_file("loading", 0222,
debug_root, host, &libahci_debug_load_ops);
/* Create subdir for each port and add there several files:
* one for port registers, one for port events log and
* two files for working with FISes
*/
for (i = 0; i < host->n_ports; i++) {
snprintf(port_n, 7, "port%02d", i);
node = debugfs_create_dir(port_n, debug_root);
debugfs_create_file("rdesc_port", 0644,
node, host->ports[i], &libahci_debug_rdesc_ops);
debugfs_create_file("events", 0644,
node, host->ports[i], &libahci_debug_events_fops);
debugfs_create_file(FILE_NAME_CFIS, 0222,
node, host->ports[i], &libahci_debug_cfis_ops);
debugfs_create_file(FILE_NAME_CHDR, 0222,
node, host->ports[i], &libahci_debug_chdr_ops);
}
if (libahci_debug_init_sg() != 0) {
goto fail_handler;
}
libahci_debug_buff_init(host->dev);
return 0;
fail_handler:
debugfs_remove_recursive(debug_root);
printk(KERN_DEBUG "%s Unable to create debugfs file structure", MARKER);
return -ENOENT;
}
EXPORT_SYMBOL_GPL(libahci_debug_init);
void libahci_debug_exit(void)
{
kfree(cmd.sg_buff);
kfree(early_buff);
debugfs_remove_recursive(debug_root);
}
EXPORT_SYMBOL_GPL(libahci_debug_exit);
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("Debug AHCI SATA low-level routines");
MODULE_LICENSE("GPL");
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/ata/libahci_debug.h 0000664 0000000 0000000 00000005656 12666203034 0027137 0 ustar 00root root 0000000 0000000 /*
* libahci_debug.h
*
* Created on: Jan 20, 2016
* Author: mk
*/
#include "ahci.h"
#ifndef _LIBAHCI_DEBUG_H_
#define _LIBAHCI_DEBUG_H_
#define ROOT_DIR_NAME "ahci_exp"
#define FILE_NAME_CFIS "cfis"
#define FILE_NAME_CHDR "chdr"
#define MARKER "++"
#define EVT_MARKER ">"
#define CMD_FIS_SZ 20
#define CMD_HDR_SZ 16
#define CMD_DMA_BUFSZ 512
#define PORT_RESERVED_2 40
#define PORT_VENDOR_BYTES 16
#define LIBAHCI_DEBUG_BUFSZ 16384
/* Total size of dump buffer in bytes*/
#define SEGMENT_SIZE 0x10000
/* The sizes below are in DWORDs*/
#define GHC_SZ 0x0B
#define PORT_REG_SZ 0x20
#define CLB_SZ 0x08
#define FIS_SZ 0x40
#define DUMP_LEN 0x240
/* The length of delimiter line in memory */
#define MARKER_LEN 0x10
/* Offset to the end of nearest 16 DWORD string */
#define ALIGN_OFFSET 0x0d
struct libahci_debug_list {
unsigned int debug;
unsigned int port_n;
char *libahci_debug_buf;
int head;
int tail;
struct list_head node;
struct mutex read_mutex;
wait_queue_head_t debug_wait;
spinlock_t debug_list_lock;
};
struct ahci_cmd_fis {
__le32 dw0;
__le32 dw1;
__le32 dw2;
__le32 dw3;
__le32 dw4;
};
struct ahci_cmd {
struct ahci_cmd_hdr hdr;
struct ahci_cmd_fis fis;
struct scatterlist sg;
char *sg_buff;
int cmd_sent;
};
struct mem_buffer {
volatile u32 *vaddr;
dma_addr_t paddr;
ssize_t size;
};
struct dump_record {
u32 reg_ghc[GHC_SZ];
u32 reg_port[PORT_REG_SZ];
u32 reg_clb[CLB_SZ];
u32 reg_fis[FIS_SZ];
u32 cntr;
};
// AHCI Port registers
struct port_regs {
// Port command list base address
u32 PxCLB;
// Port command list based address upper 32-bits
u32 PxCLBU;
// Port FIS base address
u32 PxFB;
// Port FIS base address upper 32-bits
u32 PxFBU;
u32 PxIS;
u32 PxIE;
u32 PxCMD;
u32 reserved_1;
u32 PxTFD;
u32 PxSIG;
u32 PxSSTS;
u32 PxSCTL;
u32 PxSERR;
u32 PxSACT;
u32 PxCI;
u32 PxSNTF;
u32 PxFBS;
u32 PxDEVSLP;
char reserved_2[PORT_RESERVED_2];
char PxVS[PORT_VENDOR_BYTES];
};
struct host_regs {
u32 CAP;
u32 GHC;
u32 IS;
u32 PI;
u32 VS;
u32 CCC_CTL;
u32 CCC_PORTS;
u32 EM_LOC;
u32 EM_CTL;
u32 CAP2;
u32 BOHC;
};
int libahci_debug_init(struct ata_host *host);
void libahci_debug_exit(void);
void libahci_debug_event(const struct ata_port *port ,char *msg, size_t msg_sz);
void libahci_debug_dump_region(const struct ata_port *ap, const u32 *buf, size_t buff_sz, const char* prefix);
void libahci_debug_dump_irq(u32 status);
void libahci_debug_dump_sg(const struct ata_queued_cmd *qc, const char *prefix);
void libahci_debug_irq_notify(const struct ata_port *ap);
void libahci_debug_exec_cmd(struct ata_port *ap);
void libahci_debug_wait_flag(void);
unsigned int libahci_debug_state_dump(struct ata_port *ap);
unsigned int libahci_debug_saxigp1_save(struct ata_port *ap, size_t dump_size);
#endif /* _LIBAHCI_DEBUG_H_ */
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/ata/libata-core.c 0000664 0000000 0000000 00000547032 12666203034 0026552 0 ustar 00root root 0000000 0000000 /*
* libata-core.c - helper library for ATA
*
* Maintained by: Tejun Heo
* Please ALWAYS copy linux-ide@vger.kernel.org
* on emails.
*
* Copyright 2003-2004 Red Hat, Inc. All rights reserved.
* Copyright 2003-2004 Jeff Garzik
*
*
* This program is free software; you can redistribute 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, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* libata documentation is available via 'make {ps|pdf}docs',
* as Documentation/DocBook/libata.*
*
* Hardware documentation available from http://www.t13.org/ and
* http://www.sata-io.org/
*
* Standards documents from:
* http://www.t13.org (ATA standards, PCI DMA IDE spec)
* http://www.t10.org (SCSI MMC - for ATAPI MMC)
* http://www.sata-io.org (SATA)
* http://www.compactflash.org (CF)
* http://www.qic.org (QIC157 - Tape and DSC)
* http://www.ce-ata.org (CE-ATA: not supported)
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "libata.h"
#include "libata-transport.h"
/* debounce timing parameters in msecs { interval, duration, timeout } */
const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 };
const unsigned long sata_deb_timing_hotplug[] = { 25, 500, 2000 };
const unsigned long sata_deb_timing_long[] = { 100, 2000, 5000 };
const struct ata_port_operations ata_base_port_ops = {
.prereset = ata_std_prereset,
.postreset = ata_std_postreset,
.error_handler = ata_std_error_handler,
.sched_eh = ata_std_sched_eh,
.end_eh = ata_std_end_eh,
};
const struct ata_port_operations sata_port_ops = {
.inherits = &ata_base_port_ops,
.qc_defer = ata_std_qc_defer,
.hardreset = sata_std_hardreset,
};
static unsigned int ata_dev_init_params(struct ata_device *dev,
u16 heads, u16 sectors);
static unsigned int ata_dev_set_xfermode(struct ata_device *dev);
static void ata_dev_xfermask(struct ata_device *dev);
static unsigned long ata_dev_blacklisted(const struct ata_device *dev);
atomic_t ata_print_id = ATOMIC_INIT(0);
struct ata_force_param {
const char *name;
unsigned int cbl;
int spd_limit;
unsigned long xfer_mask;
unsigned int horkage_on;
unsigned int horkage_off;
unsigned int lflags;
};
struct ata_force_ent {
int port;
int device;
struct ata_force_param param;
};
static struct ata_force_ent *ata_force_tbl;
static int ata_force_tbl_size;
static char ata_force_param_buf[PAGE_SIZE] __initdata;
/* param_buf is thrown away after initialization, disallow read */
module_param_string(force, ata_force_param_buf, sizeof(ata_force_param_buf), 0);
MODULE_PARM_DESC(force, "Force ATA configurations including cable type, link speed and transfer mode (see Documentation/kernel-parameters.txt for details)");
static int atapi_enabled = 1;
module_param(atapi_enabled, int, 0444);
MODULE_PARM_DESC(atapi_enabled, "Enable discovery of ATAPI devices (0=off, 1=on [default])");
static int atapi_dmadir = 0;
module_param(atapi_dmadir, int, 0444);
MODULE_PARM_DESC(atapi_dmadir, "Enable ATAPI DMADIR bridge support (0=off [default], 1=on)");
int atapi_passthru16 = 1;
module_param(atapi_passthru16, int, 0444);
MODULE_PARM_DESC(atapi_passthru16, "Enable ATA_16 passthru for ATAPI devices (0=off, 1=on [default])");
int libata_fua = 0;
module_param_named(fua, libata_fua, int, 0444);
MODULE_PARM_DESC(fua, "FUA support (0=off [default], 1=on)");
static int ata_ignore_hpa;
module_param_named(ignore_hpa, ata_ignore_hpa, int, 0644);
MODULE_PARM_DESC(ignore_hpa, "Ignore HPA limit (0=keep BIOS limits, 1=ignore limits, using full disk)");
static int libata_dma_mask = ATA_DMA_MASK_ATA|ATA_DMA_MASK_ATAPI|ATA_DMA_MASK_CFA;
module_param_named(dma, libata_dma_mask, int, 0444);
MODULE_PARM_DESC(dma, "DMA enable/disable (0x1==ATA, 0x2==ATAPI, 0x4==CF)");
static int ata_probe_timeout;
module_param(ata_probe_timeout, int, 0444);
MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)");
int libata_noacpi = 0;
module_param_named(noacpi, libata_noacpi, int, 0444);
MODULE_PARM_DESC(noacpi, "Disable the use of ACPI in probe/suspend/resume (0=off [default], 1=on)");
int libata_allow_tpm = 0;
module_param_named(allow_tpm, libata_allow_tpm, int, 0444);
MODULE_PARM_DESC(allow_tpm, "Permit the use of TPM commands (0=off [default], 1=on)");
static int atapi_an;
module_param(atapi_an, int, 0444);
MODULE_PARM_DESC(atapi_an, "Enable ATAPI AN media presence notification (0=0ff [default], 1=on)");
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("Library module for ATA devices");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
static bool ata_sstatus_online(u32 sstatus)
{
return (sstatus & 0xf) == 0x3;
}
/**
* ata_link_next - link iteration helper
* @link: the previous link, NULL to start
* @ap: ATA port containing links to iterate
* @mode: iteration mode, one of ATA_LITER_*
*
* LOCKING:
* Host lock or EH context.
*
* RETURNS:
* Pointer to the next link.
*/
struct ata_link *ata_link_next(struct ata_link *link, struct ata_port *ap,
enum ata_link_iter_mode mode)
{
BUG_ON(mode != ATA_LITER_EDGE &&
mode != ATA_LITER_PMP_FIRST && mode != ATA_LITER_HOST_FIRST);
/* NULL link indicates start of iteration */
if (!link)
switch (mode) {
case ATA_LITER_EDGE:
case ATA_LITER_PMP_FIRST:
if (sata_pmp_attached(ap))
return ap->pmp_link;
/* fall through */
case ATA_LITER_HOST_FIRST:
return &ap->link;
}
/* we just iterated over the host link, what's next? */
if (link == &ap->link)
switch (mode) {
case ATA_LITER_HOST_FIRST:
if (sata_pmp_attached(ap))
return ap->pmp_link;
/* fall through */
case ATA_LITER_PMP_FIRST:
if (unlikely(ap->slave_link))
return ap->slave_link;
/* fall through */
case ATA_LITER_EDGE:
return NULL;
}
/* slave_link excludes PMP */
if (unlikely(link == ap->slave_link))
return NULL;
/* we were over a PMP link */
if (++link < ap->pmp_link + ap->nr_pmp_links)
return link;
if (mode == ATA_LITER_PMP_FIRST)
return &ap->link;
return NULL;
}
/**
* ata_dev_next - device iteration helper
* @dev: the previous device, NULL to start
* @link: ATA link containing devices to iterate
* @mode: iteration mode, one of ATA_DITER_*
*
* LOCKING:
* Host lock or EH context.
*
* RETURNS:
* Pointer to the next device.
*/
struct ata_device *ata_dev_next(struct ata_device *dev, struct ata_link *link,
enum ata_dev_iter_mode mode)
{
BUG_ON(mode != ATA_DITER_ENABLED && mode != ATA_DITER_ENABLED_REVERSE &&
mode != ATA_DITER_ALL && mode != ATA_DITER_ALL_REVERSE);
/* NULL dev indicates start of iteration */
if (!dev)
switch (mode) {
case ATA_DITER_ENABLED:
case ATA_DITER_ALL:
dev = link->device;
goto check;
case ATA_DITER_ENABLED_REVERSE:
case ATA_DITER_ALL_REVERSE:
dev = link->device + ata_link_max_devices(link) - 1;
goto check;
}
next:
/* move to the next one */
switch (mode) {
case ATA_DITER_ENABLED:
case ATA_DITER_ALL:
if (++dev < link->device + ata_link_max_devices(link))
goto check;
return NULL;
case ATA_DITER_ENABLED_REVERSE:
case ATA_DITER_ALL_REVERSE:
if (--dev >= link->device)
goto check;
return NULL;
}
check:
if ((mode == ATA_DITER_ENABLED || mode == ATA_DITER_ENABLED_REVERSE) &&
!ata_dev_enabled(dev))
goto next;
return dev;
}
/**
* ata_dev_phys_link - find physical link for a device
* @dev: ATA device to look up physical link for
*
* Look up physical link which @dev is attached to. Note that
* this is different from @dev->link only when @dev is on slave
* link. For all other cases, it's the same as @dev->link.
*
* LOCKING:
* Don't care.
*
* RETURNS:
* Pointer to the found physical link.
*/
struct ata_link *ata_dev_phys_link(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
if (!ap->slave_link)
return dev->link;
if (!dev->devno)
return &ap->link;
return ap->slave_link;
}
/**
* ata_force_cbl - force cable type according to libata.force
* @ap: ATA port of interest
*
* Force cable type according to libata.force and whine about it.
* The last entry which has matching port number is used, so it
* can be specified as part of device force parameters. For
* example, both "a:40c,1.00:udma4" and "1.00:40c,udma4" have the
* same effect.
*
* LOCKING:
* EH context.
*/
void ata_force_cbl(struct ata_port *ap)
{
int i;
for (i = ata_force_tbl_size - 1; i >= 0; i--) {
const struct ata_force_ent *fe = &ata_force_tbl[i];
if (fe->port != -1 && fe->port != ap->print_id)
continue;
if (fe->param.cbl == ATA_CBL_NONE)
continue;
ap->cbl = fe->param.cbl;
ata_port_notice(ap, "FORCE: cable set to %s\n", fe->param.name);
return;
}
}
/**
* ata_force_link_limits - force link limits according to libata.force
* @link: ATA link of interest
*
* Force link flags and SATA spd limit according to libata.force
* and whine about it. When only the port part is specified
* (e.g. 1:), the limit applies to all links connected to both
* the host link and all fan-out ports connected via PMP. If the
* device part is specified as 0 (e.g. 1.00:), it specifies the
* first fan-out link not the host link. Device number 15 always
* points to the host link whether PMP is attached or not. If the
* controller has slave link, device number 16 points to it.
*
* LOCKING:
* EH context.
*/
static void ata_force_link_limits(struct ata_link *link)
{
bool did_spd = false;
int linkno = link->pmp;
int i;
if (ata_is_host_link(link))
linkno += 15;
for (i = ata_force_tbl_size - 1; i >= 0; i--) {
const struct ata_force_ent *fe = &ata_force_tbl[i];
if (fe->port != -1 && fe->port != link->ap->print_id)
continue;
if (fe->device != -1 && fe->device != linkno)
continue;
/* only honor the first spd limit */
if (!did_spd && fe->param.spd_limit) {
link->hw_sata_spd_limit = (1 << fe->param.spd_limit) - 1;
ata_link_notice(link, "FORCE: PHY spd limit set to %s\n",
fe->param.name);
did_spd = true;
}
/* let lflags stack */
if (fe->param.lflags) {
link->flags |= fe->param.lflags;
ata_link_notice(link,
"FORCE: link flag 0x%x forced -> 0x%x\n",
fe->param.lflags, link->flags);
}
}
}
/**
* ata_force_xfermask - force xfermask according to libata.force
* @dev: ATA device of interest
*
* Force xfer_mask according to libata.force and whine about it.
* For consistency with link selection, device number 15 selects
* the first device connected to the host link.
*
* LOCKING:
* EH context.
*/
static void ata_force_xfermask(struct ata_device *dev)
{
int devno = dev->link->pmp + dev->devno;
int alt_devno = devno;
int i;
/* allow n.15/16 for devices attached to host port */
if (ata_is_host_link(dev->link))
alt_devno += 15;
for (i = ata_force_tbl_size - 1; i >= 0; i--) {
const struct ata_force_ent *fe = &ata_force_tbl[i];
unsigned long pio_mask, mwdma_mask, udma_mask;
if (fe->port != -1 && fe->port != dev->link->ap->print_id)
continue;
if (fe->device != -1 && fe->device != devno &&
fe->device != alt_devno)
continue;
if (!fe->param.xfer_mask)
continue;
ata_unpack_xfermask(fe->param.xfer_mask,
&pio_mask, &mwdma_mask, &udma_mask);
if (udma_mask)
dev->udma_mask = udma_mask;
else if (mwdma_mask) {
dev->udma_mask = 0;
dev->mwdma_mask = mwdma_mask;
} else {
dev->udma_mask = 0;
dev->mwdma_mask = 0;
dev->pio_mask = pio_mask;
}
ata_dev_notice(dev, "FORCE: xfer_mask set to %s\n",
fe->param.name);
return;
}
}
/**
* ata_force_horkage - force horkage according to libata.force
* @dev: ATA device of interest
*
* Force horkage according to libata.force and whine about it.
* For consistency with link selection, device number 15 selects
* the first device connected to the host link.
*
* LOCKING:
* EH context.
*/
static void ata_force_horkage(struct ata_device *dev)
{
int devno = dev->link->pmp + dev->devno;
int alt_devno = devno;
int i;
/* allow n.15/16 for devices attached to host port */
if (ata_is_host_link(dev->link))
alt_devno += 15;
for (i = 0; i < ata_force_tbl_size; i++) {
const struct ata_force_ent *fe = &ata_force_tbl[i];
if (fe->port != -1 && fe->port != dev->link->ap->print_id)
continue;
if (fe->device != -1 && fe->device != devno &&
fe->device != alt_devno)
continue;
if (!(~dev->horkage & fe->param.horkage_on) &&
!(dev->horkage & fe->param.horkage_off))
continue;
dev->horkage |= fe->param.horkage_on;
dev->horkage &= ~fe->param.horkage_off;
ata_dev_notice(dev, "FORCE: horkage modified (%s)\n",
fe->param.name);
}
}
/**
* atapi_cmd_type - Determine ATAPI command type from SCSI opcode
* @opcode: SCSI opcode
*
* Determine ATAPI command type from @opcode.
*
* LOCKING:
* None.
*
* RETURNS:
* ATAPI_{READ|WRITE|READ_CD|PASS_THRU|MISC}
*/
int atapi_cmd_type(u8 opcode)
{
switch (opcode) {
case GPCMD_READ_10:
case GPCMD_READ_12:
return ATAPI_READ;
case GPCMD_WRITE_10:
case GPCMD_WRITE_12:
case GPCMD_WRITE_AND_VERIFY_10:
return ATAPI_WRITE;
case GPCMD_READ_CD:
case GPCMD_READ_CD_MSF:
return ATAPI_READ_CD;
case ATA_16:
case ATA_12:
if (atapi_passthru16)
return ATAPI_PASS_THRU;
/* fall thru */
default:
return ATAPI_MISC;
}
}
/**
* ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure
* @tf: Taskfile to convert
* @pmp: Port multiplier port
* @is_cmd: This FIS is for command
* @fis: Buffer into which data will output
*
* Converts a standard ATA taskfile to a Serial ATA
* FIS structure (Register - Host to Device).
*
* LOCKING:
* Inherited from caller.
*/
void ata_tf_to_fis(const struct ata_taskfile *tf, u8 pmp, int is_cmd, u8 *fis)
{
fis[0] = 0x27; /* Register - Host to Device FIS */
fis[1] = pmp & 0xf; /* Port multiplier number*/
if (is_cmd)
fis[1] |= (1 << 7); /* bit 7 indicates Command FIS */
fis[2] = tf->command;
fis[3] = tf->feature;
fis[4] = tf->lbal;
fis[5] = tf->lbam;
fis[6] = tf->lbah;
fis[7] = tf->device;
fis[8] = tf->hob_lbal;
fis[9] = tf->hob_lbam;
fis[10] = tf->hob_lbah;
fis[11] = tf->hob_feature;
fis[12] = tf->nsect;
fis[13] = tf->hob_nsect;
fis[14] = 0;
fis[15] = tf->ctl;
fis[16] = tf->auxiliary & 0xff;
fis[17] = (tf->auxiliary >> 8) & 0xff;
fis[18] = (tf->auxiliary >> 16) & 0xff;
fis[19] = (tf->auxiliary >> 24) & 0xff;
}
/**
* ata_tf_from_fis - Convert SATA FIS to ATA taskfile
* @fis: Buffer from which data will be input
* @tf: Taskfile to output
*
* Converts a serial ATA FIS structure to a standard ATA taskfile.
*
* LOCKING:
* Inherited from caller.
*/
void ata_tf_from_fis(const u8 *fis, struct ata_taskfile *tf)
{
tf->command = fis[2]; /* status */
tf->feature = fis[3]; /* error */
tf->lbal = fis[4];
tf->lbam = fis[5];
tf->lbah = fis[6];
tf->device = fis[7];
tf->hob_lbal = fis[8];
tf->hob_lbam = fis[9];
tf->hob_lbah = fis[10];
tf->nsect = fis[12];
tf->hob_nsect = fis[13];
}
static const u8 ata_rw_cmds[] = {
/* pio multi */
ATA_CMD_READ_MULTI,
ATA_CMD_WRITE_MULTI,
ATA_CMD_READ_MULTI_EXT,
ATA_CMD_WRITE_MULTI_EXT,
0,
0,
0,
ATA_CMD_WRITE_MULTI_FUA_EXT,
/* pio */
ATA_CMD_PIO_READ,
ATA_CMD_PIO_WRITE,
ATA_CMD_PIO_READ_EXT,
ATA_CMD_PIO_WRITE_EXT,
0,
0,
0,
0,
/* dma */
ATA_CMD_READ,
ATA_CMD_WRITE,
ATA_CMD_READ_EXT,
ATA_CMD_WRITE_EXT,
0,
0,
0,
ATA_CMD_WRITE_FUA_EXT
};
/**
* ata_rwcmd_protocol - set taskfile r/w commands and protocol
* @tf: command to examine and configure
* @dev: device tf belongs to
*
* Examine the device configuration and tf->flags to calculate
* the proper read/write commands and protocol to use.
*
* LOCKING:
* caller.
*/
static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev)
{
u8 cmd;
int index, fua, lba48, write;
fua = (tf->flags & ATA_TFLAG_FUA) ? 4 : 0;
lba48 = (tf->flags & ATA_TFLAG_LBA48) ? 2 : 0;
write = (tf->flags & ATA_TFLAG_WRITE) ? 1 : 0;
if (dev->flags & ATA_DFLAG_PIO) {
tf->protocol = ATA_PROT_PIO;
index = dev->multi_count ? 0 : 8;
} else if (lba48 && (dev->link->ap->flags & ATA_FLAG_PIO_LBA48)) {
/* Unable to use DMA due to host limitation */
tf->protocol = ATA_PROT_PIO;
index = dev->multi_count ? 0 : 8;
} else {
tf->protocol = ATA_PROT_DMA;
index = 16;
}
cmd = ata_rw_cmds[index + fua + lba48 + write];
if (cmd) {
tf->command = cmd;
return 0;
}
return -1;
}
/**
* ata_tf_read_block - Read block address from ATA taskfile
* @tf: ATA taskfile of interest
* @dev: ATA device @tf belongs to
*
* LOCKING:
* None.
*
* Read block address from @tf. This function can handle all
* three address formats - LBA, LBA48 and CHS. tf->protocol and
* flags select the address format to use.
*
* RETURNS:
* Block address read from @tf.
*/
u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev)
{
u64 block = 0;
if (tf->flags & ATA_TFLAG_LBA) {
if (tf->flags & ATA_TFLAG_LBA48) {
block |= (u64)tf->hob_lbah << 40;
block |= (u64)tf->hob_lbam << 32;
block |= (u64)tf->hob_lbal << 24;
} else
block |= (tf->device & 0xf) << 24;
block |= tf->lbah << 16;
block |= tf->lbam << 8;
block |= tf->lbal;
} else {
u32 cyl, head, sect;
cyl = tf->lbam | (tf->lbah << 8);
head = tf->device & 0xf;
sect = tf->lbal;
if (!sect) {
ata_dev_warn(dev,
"device reported invalid CHS sector 0\n");
sect = 1; /* oh well */
}
block = (cyl * dev->heads + head) * dev->sectors + sect - 1;
}
return block;
}
/**
* ata_build_rw_tf - Build ATA taskfile for given read/write request
* @tf: Target ATA taskfile
* @dev: ATA device @tf belongs to
* @block: Block address
* @n_block: Number of blocks
* @tf_flags: RW/FUA etc...
* @tag: tag
*
* LOCKING:
* None.
*
* Build ATA taskfile @tf for read/write request described by
* @block, @n_block, @tf_flags and @tag on @dev.
*
* RETURNS:
*
* 0 on success, -ERANGE if the request is too large for @dev,
* -EINVAL if the request is invalid.
*/
int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
u64 block, u32 n_block, unsigned int tf_flags,
unsigned int tag)
{
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf->flags |= tf_flags;
if (ata_ncq_enabled(dev) && likely(tag != ATA_TAG_INTERNAL)) {
/* yay, NCQ */
if (!lba_48_ok(block, n_block))
return -ERANGE;
tf->protocol = ATA_PROT_NCQ;
tf->flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48;
if (tf->flags & ATA_TFLAG_WRITE)
tf->command = ATA_CMD_FPDMA_WRITE;
else
tf->command = ATA_CMD_FPDMA_READ;
tf->nsect = tag << 3;
tf->hob_feature = (n_block >> 8) & 0xff;
tf->feature = n_block & 0xff;
tf->hob_lbah = (block >> 40) & 0xff;
tf->hob_lbam = (block >> 32) & 0xff;
tf->hob_lbal = (block >> 24) & 0xff;
tf->lbah = (block >> 16) & 0xff;
tf->lbam = (block >> 8) & 0xff;
tf->lbal = block & 0xff;
tf->device = ATA_LBA;
if (tf->flags & ATA_TFLAG_FUA)
tf->device |= 1 << 7;
} else if (dev->flags & ATA_DFLAG_LBA) {
tf->flags |= ATA_TFLAG_LBA;
if (lba_28_ok(block, n_block)) {
/* use LBA28 */
tf->device |= (block >> 24) & 0xf;
} else if (lba_48_ok(block, n_block)) {
if (!(dev->flags & ATA_DFLAG_LBA48))
return -ERANGE;
/* use LBA48 */
tf->flags |= ATA_TFLAG_LBA48;
tf->hob_nsect = (n_block >> 8) & 0xff;
tf->hob_lbah = (block >> 40) & 0xff;
tf->hob_lbam = (block >> 32) & 0xff;
tf->hob_lbal = (block >> 24) & 0xff;
} else
/* request too large even for LBA48 */
return -ERANGE;
if (unlikely(ata_rwcmd_protocol(tf, dev) < 0))
return -EINVAL;
tf->nsect = n_block & 0xff;
tf->lbah = (block >> 16) & 0xff;
tf->lbam = (block >> 8) & 0xff;
tf->lbal = block & 0xff;
tf->device |= ATA_LBA;
} else {
/* CHS */
u32 sect, head, cyl, track;
/* The request -may- be too large for CHS addressing. */
if (!lba_28_ok(block, n_block))
return -ERANGE;
if (unlikely(ata_rwcmd_protocol(tf, dev) < 0))
return -EINVAL;
/* Convert LBA to CHS */
track = (u32)block / dev->sectors;
cyl = track / dev->heads;
head = track % dev->heads;
sect = (u32)block % dev->sectors + 1;
DPRINTK("block %u track %u cyl %u head %u sect %u\n",
(u32)block, track, cyl, head, sect);
/* Check whether the converted CHS can fit.
Cylinder: 0-65535
Head: 0-15
Sector: 1-255*/
if ((cyl >> 16) || (head >> 4) || (sect >> 8) || (!sect))
return -ERANGE;
tf->nsect = n_block & 0xff; /* Sector count 0 means 256 sectors */
tf->lbal = sect;
tf->lbam = cyl;
tf->lbah = cyl >> 8;
tf->device |= head;
}
return 0;
}
/**
* ata_pack_xfermask - Pack pio, mwdma and udma masks into xfer_mask
* @pio_mask: pio_mask
* @mwdma_mask: mwdma_mask
* @udma_mask: udma_mask
*
* Pack @pio_mask, @mwdma_mask and @udma_mask into a single
* unsigned int xfer_mask.
*
* LOCKING:
* None.
*
* RETURNS:
* Packed xfer_mask.
*/
unsigned long ata_pack_xfermask(unsigned long pio_mask,
unsigned long mwdma_mask,
unsigned long udma_mask)
{
return ((pio_mask << ATA_SHIFT_PIO) & ATA_MASK_PIO) |
((mwdma_mask << ATA_SHIFT_MWDMA) & ATA_MASK_MWDMA) |
((udma_mask << ATA_SHIFT_UDMA) & ATA_MASK_UDMA);
}
/**
* ata_unpack_xfermask - Unpack xfer_mask into pio, mwdma and udma masks
* @xfer_mask: xfer_mask to unpack
* @pio_mask: resulting pio_mask
* @mwdma_mask: resulting mwdma_mask
* @udma_mask: resulting udma_mask
*
* Unpack @xfer_mask into @pio_mask, @mwdma_mask and @udma_mask.
* Any NULL distination masks will be ignored.
*/
void ata_unpack_xfermask(unsigned long xfer_mask, unsigned long *pio_mask,
unsigned long *mwdma_mask, unsigned long *udma_mask)
{
if (pio_mask)
*pio_mask = (xfer_mask & ATA_MASK_PIO) >> ATA_SHIFT_PIO;
if (mwdma_mask)
*mwdma_mask = (xfer_mask & ATA_MASK_MWDMA) >> ATA_SHIFT_MWDMA;
if (udma_mask)
*udma_mask = (xfer_mask & ATA_MASK_UDMA) >> ATA_SHIFT_UDMA;
}
static const struct ata_xfer_ent {
int shift, bits;
u8 base;
} ata_xfer_tbl[] = {
{ ATA_SHIFT_PIO, ATA_NR_PIO_MODES, XFER_PIO_0 },
{ ATA_SHIFT_MWDMA, ATA_NR_MWDMA_MODES, XFER_MW_DMA_0 },
{ ATA_SHIFT_UDMA, ATA_NR_UDMA_MODES, XFER_UDMA_0 },
{ -1, },
};
/**
* ata_xfer_mask2mode - Find matching XFER_* for the given xfer_mask
* @xfer_mask: xfer_mask of interest
*
* Return matching XFER_* value for @xfer_mask. Only the highest
* bit of @xfer_mask is considered.
*
* LOCKING:
* None.
*
* RETURNS:
* Matching XFER_* value, 0xff if no match found.
*/
u8 ata_xfer_mask2mode(unsigned long xfer_mask)
{
int highbit = fls(xfer_mask) - 1;
const struct ata_xfer_ent *ent;
for (ent = ata_xfer_tbl; ent->shift >= 0; ent++)
if (highbit >= ent->shift && highbit < ent->shift + ent->bits)
return ent->base + highbit - ent->shift;
return 0xff;
}
/**
* ata_xfer_mode2mask - Find matching xfer_mask for XFER_*
* @xfer_mode: XFER_* of interest
*
* Return matching xfer_mask for @xfer_mode.
*
* LOCKING:
* None.
*
* RETURNS:
* Matching xfer_mask, 0 if no match found.
*/
unsigned long ata_xfer_mode2mask(u8 xfer_mode)
{
const struct ata_xfer_ent *ent;
for (ent = ata_xfer_tbl; ent->shift >= 0; ent++)
if (xfer_mode >= ent->base && xfer_mode < ent->base + ent->bits)
return ((2 << (ent->shift + xfer_mode - ent->base)) - 1)
& ~((1 << ent->shift) - 1);
return 0;
}
/**
* ata_xfer_mode2shift - Find matching xfer_shift for XFER_*
* @xfer_mode: XFER_* of interest
*
* Return matching xfer_shift for @xfer_mode.
*
* LOCKING:
* None.
*
* RETURNS:
* Matching xfer_shift, -1 if no match found.
*/
int ata_xfer_mode2shift(unsigned long xfer_mode)
{
const struct ata_xfer_ent *ent;
for (ent = ata_xfer_tbl; ent->shift >= 0; ent++)
if (xfer_mode >= ent->base && xfer_mode < ent->base + ent->bits)
return ent->shift;
return -1;
}
/**
* ata_mode_string - convert xfer_mask to string
* @xfer_mask: mask of bits supported; only highest bit counts.
*
* Determine string which represents the highest speed
* (highest bit in @modemask).
*
* LOCKING:
* None.
*
* RETURNS:
* Constant C string representing highest speed listed in
* @mode_mask, or the constant C string "".
*/
const char *ata_mode_string(unsigned long xfer_mask)
{
static const char * const xfer_mode_str[] = {
"PIO0",
"PIO1",
"PIO2",
"PIO3",
"PIO4",
"PIO5",
"PIO6",
"MWDMA0",
"MWDMA1",
"MWDMA2",
"MWDMA3",
"MWDMA4",
"UDMA/16",
"UDMA/25",
"UDMA/33",
"UDMA/44",
"UDMA/66",
"UDMA/100",
"UDMA/133",
"UDMA7",
};
int highbit;
highbit = fls(xfer_mask) - 1;
if (highbit >= 0 && highbit < ARRAY_SIZE(xfer_mode_str))
return xfer_mode_str[highbit];
return "";
}
const char *sata_spd_string(unsigned int spd)
{
static const char * const spd_str[] = {
"1.5 Gbps",
"3.0 Gbps",
"6.0 Gbps",
};
if (spd == 0 || (spd - 1) >= ARRAY_SIZE(spd_str))
return "";
return spd_str[spd - 1];
}
/**
* ata_dev_classify - determine device type based on ATA-spec signature
* @tf: ATA taskfile register set for device to be identified
*
* Determine from taskfile register contents whether a device is
* ATA or ATAPI, as per "Signature and persistence" section
* of ATA/PI spec (volume 1, sect 5.14).
*
* LOCKING:
* None.
*
* RETURNS:
* Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, %ATA_DEV_PMP,
* %ATA_DEV_ZAC, or %ATA_DEV_UNKNOWN the event of failure.
*/
unsigned int ata_dev_classify(const struct ata_taskfile *tf)
{
/* Apple's open source Darwin code hints that some devices only
* put a proper signature into the LBA mid/high registers,
* So, we only check those. It's sufficient for uniqueness.
*
* ATA/ATAPI-7 (d1532v1r1: Feb. 19, 2003) specified separate
* signatures for ATA and ATAPI devices attached on SerialATA,
* 0x3c/0xc3 and 0x69/0x96 respectively. However, SerialATA
* spec has never mentioned about using different signatures
* for ATA/ATAPI devices. Then, Serial ATA II: Port
* Multiplier specification began to use 0x69/0x96 to identify
* port multpliers and 0x3c/0xc3 to identify SEMB device.
* ATA/ATAPI-7 dropped descriptions about 0x3c/0xc3 and
* 0x69/0x96 shortly and described them as reserved for
* SerialATA.
*
* We follow the current spec and consider that 0x69/0x96
* identifies a port multiplier and 0x3c/0xc3 a SEMB device.
* Unfortunately, WDC WD1600JS-62MHB5 (a hard drive) reports
* SEMB signature. This is worked around in
* ata_dev_read_id().
*/
if ((tf->lbam == 0) && (tf->lbah == 0)) {
DPRINTK("found ATA device by sig\n");
return ATA_DEV_ATA;
}
if ((tf->lbam == 0x14) && (tf->lbah == 0xeb)) {
DPRINTK("found ATAPI device by sig\n");
return ATA_DEV_ATAPI;
}
if ((tf->lbam == 0x69) && (tf->lbah == 0x96)) {
DPRINTK("found PMP device by sig\n");
return ATA_DEV_PMP;
}
if ((tf->lbam == 0x3c) && (tf->lbah == 0xc3)) {
DPRINTK("found SEMB device by sig (could be ATA device)\n");
return ATA_DEV_SEMB;
}
if ((tf->lbam == 0xcd) && (tf->lbah == 0xab)) {
DPRINTK("found ZAC device by sig\n");
return ATA_DEV_ZAC;
}
DPRINTK("unknown device\n");
return ATA_DEV_UNKNOWN;
}
/**
* ata_id_string - Convert IDENTIFY DEVICE page into string
* @id: IDENTIFY DEVICE results we will examine
* @s: string into which data is output
* @ofs: offset into identify device page
* @len: length of string to return. must be an even number.
*
* The strings in the IDENTIFY DEVICE page are broken up into
* 16-bit chunks. Run through the string, and output each
* 8-bit chunk linearly, regardless of platform.
*
* LOCKING:
* caller.
*/
void ata_id_string(const u16 *id, unsigned char *s,
unsigned int ofs, unsigned int len)
{
unsigned int c;
BUG_ON(len & 1);
while (len > 0) {
c = id[ofs] >> 8;
*s = c;
s++;
c = id[ofs] & 0xff;
*s = c;
s++;
ofs++;
len -= 2;
}
}
/**
* ata_id_c_string - Convert IDENTIFY DEVICE page into C string
* @id: IDENTIFY DEVICE results we will examine
* @s: string into which data is output
* @ofs: offset into identify device page
* @len: length of string to return. must be an odd number.
*
* This function is identical to ata_id_string except that it
* trims trailing spaces and terminates the resulting string with
* null. @len must be actual maximum length (even number) + 1.
*
* LOCKING:
* caller.
*/
void ata_id_c_string(const u16 *id, unsigned char *s,
unsigned int ofs, unsigned int len)
{
unsigned char *p;
ata_id_string(id, s, ofs, len - 1);
p = s + strnlen(s, len - 1);
while (p > s && p[-1] == ' ')
p--;
*p = '\0';
}
static u64 ata_id_n_sectors(const u16 *id)
{
if (ata_id_has_lba(id)) {
if (ata_id_has_lba48(id))
return ata_id_u64(id, ATA_ID_LBA_CAPACITY_2);
else
return ata_id_u32(id, ATA_ID_LBA_CAPACITY);
} else {
if (ata_id_current_chs_valid(id))
return id[ATA_ID_CUR_CYLS] * id[ATA_ID_CUR_HEADS] *
id[ATA_ID_CUR_SECTORS];
else
return id[ATA_ID_CYLS] * id[ATA_ID_HEADS] *
id[ATA_ID_SECTORS];
}
}
u64 ata_tf_to_lba48(const struct ata_taskfile *tf)
{
u64 sectors = 0;
sectors |= ((u64)(tf->hob_lbah & 0xff)) << 40;
sectors |= ((u64)(tf->hob_lbam & 0xff)) << 32;
sectors |= ((u64)(tf->hob_lbal & 0xff)) << 24;
sectors |= (tf->lbah & 0xff) << 16;
sectors |= (tf->lbam & 0xff) << 8;
sectors |= (tf->lbal & 0xff);
return sectors;
}
u64 ata_tf_to_lba(const struct ata_taskfile *tf)
{
u64 sectors = 0;
sectors |= (tf->device & 0x0f) << 24;
sectors |= (tf->lbah & 0xff) << 16;
sectors |= (tf->lbam & 0xff) << 8;
sectors |= (tf->lbal & 0xff);
return sectors;
}
/**
* ata_read_native_max_address - Read native max address
* @dev: target device
* @max_sectors: out parameter for the result native max address
*
* Perform an LBA48 or LBA28 native size query upon the device in
* question.
*
* RETURNS:
* 0 on success, -EACCES if command is aborted by the drive.
* -EIO on other errors.
*/
static int ata_read_native_max_address(struct ata_device *dev, u64 *max_sectors)
{
unsigned int err_mask;
struct ata_taskfile tf;
int lba48 = ata_id_has_lba48(dev->id);
ata_tf_init(dev, &tf);
/* always clear all address registers */
tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
if (lba48) {
tf.command = ATA_CMD_READ_NATIVE_MAX_EXT;
tf.flags |= ATA_TFLAG_LBA48;
} else
tf.command = ATA_CMD_READ_NATIVE_MAX;
tf.protocol |= ATA_PROT_NODATA;
tf.device |= ATA_LBA;
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
if (err_mask) {
ata_dev_warn(dev,
"failed to read native max address (err_mask=0x%x)\n",
err_mask);
if (err_mask == AC_ERR_DEV && (tf.feature & ATA_ABORTED))
return -EACCES;
return -EIO;
}
if (lba48)
*max_sectors = ata_tf_to_lba48(&tf) + 1;
else
*max_sectors = ata_tf_to_lba(&tf) + 1;
if (dev->horkage & ATA_HORKAGE_HPA_SIZE)
(*max_sectors)--;
return 0;
}
/**
* ata_set_max_sectors - Set max sectors
* @dev: target device
* @new_sectors: new max sectors value to set for the device
*
* Set max sectors of @dev to @new_sectors.
*
* RETURNS:
* 0 on success, -EACCES if command is aborted or denied (due to
* previous non-volatile SET_MAX) by the drive. -EIO on other
* errors.
*/
static int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors)
{
unsigned int err_mask;
struct ata_taskfile tf;
int lba48 = ata_id_has_lba48(dev->id);
new_sectors--;
ata_tf_init(dev, &tf);
tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
if (lba48) {
tf.command = ATA_CMD_SET_MAX_EXT;
tf.flags |= ATA_TFLAG_LBA48;
tf.hob_lbal = (new_sectors >> 24) & 0xff;
tf.hob_lbam = (new_sectors >> 32) & 0xff;
tf.hob_lbah = (new_sectors >> 40) & 0xff;
} else {
tf.command = ATA_CMD_SET_MAX;
tf.device |= (new_sectors >> 24) & 0xf;
}
tf.protocol |= ATA_PROT_NODATA;
tf.device |= ATA_LBA;
tf.lbal = (new_sectors >> 0) & 0xff;
tf.lbam = (new_sectors >> 8) & 0xff;
tf.lbah = (new_sectors >> 16) & 0xff;
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
if (err_mask) {
ata_dev_warn(dev,
"failed to set max address (err_mask=0x%x)\n",
err_mask);
if (err_mask == AC_ERR_DEV &&
(tf.feature & (ATA_ABORTED | ATA_IDNF)))
return -EACCES;
return -EIO;
}
return 0;
}
/**
* ata_hpa_resize - Resize a device with an HPA set
* @dev: Device to resize
*
* Read the size of an LBA28 or LBA48 disk with HPA features and resize
* it if required to the full size of the media. The caller must check
* the drive has the HPA feature set enabled.
*
* RETURNS:
* 0 on success, -errno on failure.
*/
static int ata_hpa_resize(struct ata_device *dev)
{
struct ata_eh_context *ehc = &dev->link->eh_context;
int print_info = ehc->i.flags & ATA_EHI_PRINTINFO;
bool unlock_hpa = ata_ignore_hpa || dev->flags & ATA_DFLAG_UNLOCK_HPA;
u64 sectors = ata_id_n_sectors(dev->id);
u64 native_sectors;
int rc;
/* do we need to do it? */
if ((dev->class != ATA_DEV_ATA && dev->class != ATA_DEV_ZAC) ||
!ata_id_has_lba(dev->id) || !ata_id_hpa_enabled(dev->id) ||
(dev->horkage & ATA_HORKAGE_BROKEN_HPA))
return 0;
/* read native max address */
rc = ata_read_native_max_address(dev, &native_sectors);
if (rc) {
/* If device aborted the command or HPA isn't going to
* be unlocked, skip HPA resizing.
*/
if (rc == -EACCES || !unlock_hpa) {
ata_dev_warn(dev,
"HPA support seems broken, skipping HPA handling\n");
dev->horkage |= ATA_HORKAGE_BROKEN_HPA;
/* we can continue if device aborted the command */
if (rc == -EACCES)
rc = 0;
}
return rc;
}
dev->n_native_sectors = native_sectors;
/* nothing to do? */
if (native_sectors <= sectors || !unlock_hpa) {
if (!print_info || native_sectors == sectors)
return 0;
if (native_sectors > sectors)
ata_dev_info(dev,
"HPA detected: current %llu, native %llu\n",
(unsigned long long)sectors,
(unsigned long long)native_sectors);
else if (native_sectors < sectors)
ata_dev_warn(dev,
"native sectors (%llu) is smaller than sectors (%llu)\n",
(unsigned long long)native_sectors,
(unsigned long long)sectors);
return 0;
}
/* let's unlock HPA */
rc = ata_set_max_sectors(dev, native_sectors);
if (rc == -EACCES) {
/* if device aborted the command, skip HPA resizing */
ata_dev_warn(dev,
"device aborted resize (%llu -> %llu), skipping HPA handling\n",
(unsigned long long)sectors,
(unsigned long long)native_sectors);
dev->horkage |= ATA_HORKAGE_BROKEN_HPA;
return 0;
} else if (rc)
return rc;
/* re-read IDENTIFY data */
rc = ata_dev_reread_id(dev, 0);
if (rc) {
ata_dev_err(dev,
"failed to re-read IDENTIFY data after HPA resizing\n");
return rc;
}
if (print_info) {
u64 new_sectors = ata_id_n_sectors(dev->id);
ata_dev_info(dev,
"HPA unlocked: %llu -> %llu, native %llu\n",
(unsigned long long)sectors,
(unsigned long long)new_sectors,
(unsigned long long)native_sectors);
}
return 0;
}
/**
* ata_dump_id - IDENTIFY DEVICE info debugging output
* @id: IDENTIFY DEVICE page to dump
*
* Dump selected 16-bit words from the given IDENTIFY DEVICE
* page.
*
* LOCKING:
* caller.
*/
static inline void ata_dump_id(const u16 *id)
{
DPRINTK("49==0x%04x "
"53==0x%04x "
"63==0x%04x "
"64==0x%04x "
"75==0x%04x \n",
id[49],
id[53],
id[63],
id[64],
id[75]);
DPRINTK("80==0x%04x "
"81==0x%04x "
"82==0x%04x "
"83==0x%04x "
"84==0x%04x \n",
id[80],
id[81],
id[82],
id[83],
id[84]);
DPRINTK("88==0x%04x "
"93==0x%04x\n",
id[88],
id[93]);
}
/**
* ata_id_xfermask - Compute xfermask from the given IDENTIFY data
* @id: IDENTIFY data to compute xfer mask from
*
* Compute the xfermask for this device. This is not as trivial
* as it seems if we must consider early devices correctly.
*
* FIXME: pre IDE drive timing (do we care ?).
*
* LOCKING:
* None.
*
* RETURNS:
* Computed xfermask
*/
unsigned long ata_id_xfermask(const u16 *id)
{
unsigned long pio_mask, mwdma_mask, udma_mask;
/* Usual case. Word 53 indicates word 64 is valid */
if (id[ATA_ID_FIELD_VALID] & (1 << 1)) {
pio_mask = id[ATA_ID_PIO_MODES] & 0x03;
pio_mask <<= 3;
pio_mask |= 0x7;
} else {
/* If word 64 isn't valid then Word 51 high byte holds
* the PIO timing number for the maximum. Turn it into
* a mask.
*/
u8 mode = (id[ATA_ID_OLD_PIO_MODES] >> 8) & 0xFF;
if (mode < 5) /* Valid PIO range */
pio_mask = (2 << mode) - 1;
else
pio_mask = 1;
/* But wait.. there's more. Design your standards by
* committee and you too can get a free iordy field to
* process. However its the speeds not the modes that
* are supported... Note drivers using the timing API
* will get this right anyway
*/
}
mwdma_mask = id[ATA_ID_MWDMA_MODES] & 0x07;
if (ata_id_is_cfa(id)) {
/*
* Process compact flash extended modes
*/
int pio = (id[ATA_ID_CFA_MODES] >> 0) & 0x7;
int dma = (id[ATA_ID_CFA_MODES] >> 3) & 0x7;
if (pio)
pio_mask |= (1 << 5);
if (pio > 1)
pio_mask |= (1 << 6);
if (dma)
mwdma_mask |= (1 << 3);
if (dma > 1)
mwdma_mask |= (1 << 4);
}
udma_mask = 0;
if (id[ATA_ID_FIELD_VALID] & (1 << 2))
udma_mask = id[ATA_ID_UDMA_MODES] & 0xff;
return ata_pack_xfermask(pio_mask, mwdma_mask, udma_mask);
}
static void ata_qc_complete_internal(struct ata_queued_cmd *qc)
{
struct completion *waiting = qc->private_data;
complete(waiting);
}
/**
* ata_exec_internal_sg - execute libata internal command
* @dev: Device to which the command is sent
* @tf: Taskfile registers for the command and the result
* @cdb: CDB for packet command
* @dma_dir: Data transfer direction of the command
* @sgl: sg list for the data buffer of the command
* @n_elem: Number of sg entries
* @timeout: Timeout in msecs (0 for default)
*
* Executes libata internal command with timeout. @tf contains
* command on entry and result on return. Timeout and error
* conditions are reported via return value. No recovery action
* is taken after a command times out. It's caller's duty to
* clean up after timeout.
*
* LOCKING:
* None. Should be called with kernel context, might sleep.
*
* RETURNS:
* Zero on success, AC_ERR_* mask on failure
*/
unsigned ata_exec_internal_sg(struct ata_device *dev,
struct ata_taskfile *tf, const u8 *cdb,
int dma_dir, struct scatterlist *sgl,
unsigned int n_elem, unsigned long timeout)
{
struct ata_link *link = dev->link;
struct ata_port *ap = link->ap;
u8 command = tf->command;
int auto_timeout = 0;
struct ata_queued_cmd *qc;
unsigned int tag, preempted_tag;
u32 preempted_sactive, preempted_qc_active;
int preempted_nr_active_links;
DECLARE_COMPLETION_ONSTACK(wait);
unsigned long flags;
unsigned int err_mask;
int rc;
dev_info(ap->host->dev, "libata-core.c:ata_exec_internal_sg() ap->pflags = 0x%x, link->eh_info.action=0x%08x\n",ap->pflags,link->eh_info.action);
spin_lock_irqsave(ap->lock, flags);
/* no internal command while frozen */
if (ap->pflags & ATA_PFLAG_FROZEN) {
spin_unlock_irqrestore(ap->lock, flags);
return AC_ERR_SYSTEM;
}
/* initialize internal qc */
/* XXX: Tag 0 is used for drivers with legacy EH as some
* drivers choke if any other tag is given. This breaks
* ata_tag_internal() test for those drivers. Don't use new
* EH stuff without converting to it.
*/
if (ap->ops->error_handler)
tag = ATA_TAG_INTERNAL;
else
tag = 0;
// Elphel: Could not find where 5 LSB of CAP register (number of available slots) is used. x393_sata has only one slot - slot # 0
tag = 0;
qc = __ata_qc_from_tag(ap, tag);
qc->tag = tag;
qc->scsicmd = NULL;
qc->ap = ap;
qc->dev = dev;
ata_qc_reinit(qc);
preempted_tag = link->active_tag;
preempted_sactive = link->sactive;
preempted_qc_active = ap->qc_active;
preempted_nr_active_links = ap->nr_active_links;
link->active_tag = ATA_TAG_POISON;
link->sactive = 0;
ap->qc_active = 0;
ap->nr_active_links = 0;
/* prepare & issue qc */
qc->tf = *tf;
if (cdb)
memcpy(qc->cdb, cdb, ATAPI_CDB_LEN);
/* some SATA bridges need us to indicate data xfer direction */
if (tf->protocol == ATAPI_PROT_DMA && (dev->flags & ATA_DFLAG_DMADIR) &&
dma_dir == DMA_FROM_DEVICE)
qc->tf.feature |= ATAPI_DMADIR;
qc->flags |= ATA_QCFLAG_RESULT_TF;
qc->dma_dir = dma_dir;
if (dma_dir != DMA_NONE) {
unsigned int i, buflen = 0;
struct scatterlist *sg;
for_each_sg(sgl, sg, n_elem, i)
buflen += sg->length;
ata_sg_init(qc, sgl, n_elem);
qc->nbytes = buflen;
}
qc->private_data = &wait;
qc->complete_fn = ata_qc_complete_internal;
ata_qc_issue(qc);
spin_unlock_irqrestore(ap->lock, flags);
dev_info(ap->host->dev, "libata-core.c:ata_exec_internal_sg(), timeout = 0x%x, qc->err_mask= 0x%08x, qc->flags=0x%08x, link->eh_info.action=0x%08x\n",
timeout, qc->err_mask, qc->flags, link->eh_info.action);
//link->eh_info.action
if (!timeout) {
if (ata_probe_timeout)
timeout = ata_probe_timeout * 1000;
else {
timeout = ata_internal_cmd_timeout(dev, command);
auto_timeout = 1;
}
}
if (ap->ops->error_handler)
ata_eh_release(ap);
rc = wait_for_completion_timeout(&wait, msecs_to_jiffies(timeout));
if (ap->ops->error_handler)
ata_eh_acquire(ap);
ata_sff_flush_pio_task(ap);
if (!rc) {
spin_lock_irqsave(ap->lock, flags);
/* We're racing with irq here. If we lose, the
* following test prevents us from completing the qc
* twice. If we win, the port is frozen and will be
* cleaned up by ->post_internal_cmd().
*/
if (qc->flags & ATA_QCFLAG_ACTIVE) {
qc->err_mask |= AC_ERR_TIMEOUT;
if (ap->ops->error_handler)
ata_port_freeze(ap);
else
ata_qc_complete(qc);
if (ata_msg_warn(ap))
ata_dev_warn(dev, "qc timeout (cmd 0x%x)\n",
command);
}
spin_unlock_irqrestore(ap->lock, flags);
}
/* do post_internal_cmd */
if (ap->ops->post_internal_cmd)
ap->ops->post_internal_cmd(qc);
/* perform minimal error analysis */
if (qc->flags & ATA_QCFLAG_FAILED) {
if (qc->result_tf.command & (ATA_ERR | ATA_DF))
qc->err_mask |= AC_ERR_DEV;
if (!qc->err_mask)
qc->err_mask |= AC_ERR_OTHER;
if (qc->err_mask & ~AC_ERR_OTHER)
qc->err_mask &= ~AC_ERR_OTHER;
}
/* finish up */
spin_lock_irqsave(ap->lock, flags);
*tf = qc->result_tf;
err_mask = qc->err_mask;
ata_qc_free(qc);
link->active_tag = preempted_tag;
link->sactive = preempted_sactive;
ap->qc_active = preempted_qc_active;
ap->nr_active_links = preempted_nr_active_links;
spin_unlock_irqrestore(ap->lock, flags);
if ((err_mask & AC_ERR_TIMEOUT) && auto_timeout)
ata_internal_cmd_timed_out(dev, command);
return err_mask;
}
/**
* ata_exec_internal - execute libata internal command
* @dev: Device to which the command is sent
* @tf: Taskfile registers for the command and the result
* @cdb: CDB for packet command
* @dma_dir: Data transfer direction of the command
* @buf: Data buffer of the command
* @buflen: Length of data buffer
* @timeout: Timeout in msecs (0 for default)
*
* Wrapper around ata_exec_internal_sg() which takes simple
* buffer instead of sg list.
*
* LOCKING:
* None. Should be called with kernel context, might sleep.
*
* RETURNS:
* Zero on success, AC_ERR_* mask on failure
*/
unsigned ata_exec_internal(struct ata_device *dev,
struct ata_taskfile *tf, const u8 *cdb,
int dma_dir, void *buf, unsigned int buflen,
unsigned long timeout)
{
struct scatterlist *psg = NULL, sg;
unsigned int n_elem = 0;
dev_info(dev->link->ap->host->dev, "libata-core.c ata_exec_internal()");
if (dma_dir != DMA_NONE) {
WARN_ON(!buf);
sg_init_one(&sg, buf, buflen);
psg = &sg;
n_elem++;
}
return ata_exec_internal_sg(dev, tf, cdb, dma_dir, psg, n_elem,
timeout);
}
/**
* ata_pio_need_iordy - check if iordy needed
* @adev: ATA device
*
* Check if the current speed of the device requires IORDY. Used
* by various controllers for chip configuration.
*/
unsigned int ata_pio_need_iordy(const struct ata_device *adev)
{
/* Don't set IORDY if we're preparing for reset. IORDY may
* lead to controller lock up on certain controllers if the
* port is not occupied. See bko#11703 for details.
*/
if (adev->link->ap->pflags & ATA_PFLAG_RESETTING)
return 0;
/* Controller doesn't support IORDY. Probably a pointless
* check as the caller should know this.
*/
if (adev->link->ap->flags & ATA_FLAG_NO_IORDY)
return 0;
/* CF spec. r4.1 Table 22 says no iordy on PIO5 and PIO6. */
if (ata_id_is_cfa(adev->id)
&& (adev->pio_mode == XFER_PIO_5 || adev->pio_mode == XFER_PIO_6))
return 0;
/* PIO3 and higher it is mandatory */
if (adev->pio_mode > XFER_PIO_2)
return 1;
/* We turn it on when possible */
if (ata_id_has_iordy(adev->id))
return 1;
return 0;
}
/**
* ata_pio_mask_no_iordy - Return the non IORDY mask
* @adev: ATA device
*
* Compute the highest mode possible if we are not using iordy. Return
* -1 if no iordy mode is available.
*/
static u32 ata_pio_mask_no_iordy(const struct ata_device *adev)
{
/* If we have no drive specific rule, then PIO 2 is non IORDY */
if (adev->id[ATA_ID_FIELD_VALID] & 2) { /* EIDE */
u16 pio = adev->id[ATA_ID_EIDE_PIO];
/* Is the speed faster than the drive allows non IORDY ? */
if (pio) {
/* This is cycle times not frequency - watch the logic! */
if (pio > 240) /* PIO2 is 240nS per cycle */
return 3 << ATA_SHIFT_PIO;
return 7 << ATA_SHIFT_PIO;
}
}
return 3 << ATA_SHIFT_PIO;
}
/**
* ata_do_dev_read_id - default ID read method
* @dev: device
* @tf: proposed taskfile
* @id: data buffer
*
* Issue the identify taskfile and hand back the buffer containing
* identify data. For some RAID controllers and for pre ATA devices
* this function is wrapped or replaced by the driver
*/
unsigned int ata_do_dev_read_id(struct ata_device *dev,
struct ata_taskfile *tf, u16 *id)
{
dev_info(dev->link->ap->host->dev, "libata-core.c ata_do_dev_read_id()");
return ata_exec_internal(dev, tf, NULL, DMA_FROM_DEVICE,
id, sizeof(id[0]) * ATA_ID_WORDS, 0);
}
/**
* ata_dev_read_id - Read ID data from the specified device
* @dev: target device
* @p_class: pointer to class of the target device (may be changed)
* @flags: ATA_READID_* flags
* @id: buffer to read IDENTIFY data into
*
* Read ID data from the specified device. ATA_CMD_ID_ATA is
* performed on ATA devices and ATA_CMD_ID_ATAPI on ATAPI
* devices. This function also issues ATA_CMD_INIT_DEV_PARAMS
* for pre-ATA4 drives.
*
* FIXME: ATA_CMD_ID_ATA is optional for early drives and right
* now we abort if we hit that case.
*
* LOCKING:
* Kernel thread context (may sleep)
*
* RETURNS:
* 0 on success, -errno otherwise.
*/
int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class,
unsigned int flags, u16 *id)
{
struct ata_port *ap = dev->link->ap;
unsigned int class = *p_class;
struct ata_taskfile tf;
unsigned int err_mask = 0;
const char *reason;
bool is_semb = class == ATA_DEV_SEMB;
int may_fallback = 1, tried_spinup = 0;
int rc;
if (ata_msg_ctl(ap))
ata_dev_dbg(dev, "%s: ENTER\n", __func__);
retry:
ata_tf_init(dev, &tf);
switch (class) {
case ATA_DEV_SEMB:
class = ATA_DEV_ATA; /* some hard drives report SEMB sig */
case ATA_DEV_ATA:
case ATA_DEV_ZAC:
tf.command = ATA_CMD_ID_ATA;
break;
case ATA_DEV_ATAPI:
tf.command = ATA_CMD_ID_ATAPI;
break;
default:
rc = -ENODEV;
reason = "unsupported class";
goto err_out;
}
tf.protocol = ATA_PROT_PIO;
/* Some devices choke if TF registers contain garbage. Make
* sure those are properly initialized.
*/
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
/* Device presence detection is unreliable on some
* controllers. Always poll IDENTIFY if available.
*/
tf.flags |= ATA_TFLAG_POLLING;
if (ap->ops->read_id)
err_mask = ap->ops->read_id(dev, &tf, id);
else
err_mask = ata_do_dev_read_id(dev, &tf, id);
if (err_mask) {
if (err_mask & AC_ERR_NODEV_HINT) {
ata_dev_dbg(dev, "NODEV after polling detection\n");
return -ENOENT;
}
if (is_semb) {
ata_dev_info(dev,
"IDENTIFY failed on device w/ SEMB sig, disabled\n");
/* SEMB is not supported yet */
*p_class = ATA_DEV_SEMB_UNSUP;
return 0;
}
if ((err_mask == AC_ERR_DEV) && (tf.feature & ATA_ABORTED)) {
/* Device or controller might have reported
* the wrong device class. Give a shot at the
* other IDENTIFY if the current one is
* aborted by the device.
*/
if (may_fallback) {
may_fallback = 0;
if (class == ATA_DEV_ATA)
class = ATA_DEV_ATAPI;
else
class = ATA_DEV_ATA;
goto retry;
}
/* Control reaches here iff the device aborted
* both flavors of IDENTIFYs which happens
* sometimes with phantom devices.
*/
ata_dev_dbg(dev,
"both IDENTIFYs aborted, assuming NODEV\n");
return -ENOENT;
}
rc = -EIO;
reason = "I/O error";
goto err_out;
}
if (dev->horkage & ATA_HORKAGE_DUMP_ID) {
ata_dev_dbg(dev, "dumping IDENTIFY data, "
"class=%d may_fallback=%d tried_spinup=%d\n",
class, may_fallback, tried_spinup);
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,
16, 2, id, ATA_ID_WORDS * sizeof(*id), true);
}
/* Falling back doesn't make sense if ID data was read
* successfully at least once.
*/
may_fallback = 0;
swap_buf_le16(id, ATA_ID_WORDS);
/* sanity check */
rc = -EINVAL;
reason = "device reports invalid type";
if (class == ATA_DEV_ATA || class == ATA_DEV_ZAC) {
if (!ata_id_is_ata(id) && !ata_id_is_cfa(id))
goto err_out;
if (ap->host->flags & ATA_HOST_IGNORE_ATA &&
ata_id_is_ata(id)) {
ata_dev_dbg(dev,
"host indicates ignore ATA devices, ignored\n");
return -ENOENT;
}
} else {
if (ata_id_is_ata(id))
goto err_out;
}
if (!tried_spinup && (id[2] == 0x37c8 || id[2] == 0x738c)) {
tried_spinup = 1;
/*
* Drive powered-up in standby mode, and requires a specific
* SET_FEATURES spin-up subcommand before it will accept
* anything other than the original IDENTIFY command.
*/
err_mask = ata_dev_set_feature(dev, SETFEATURES_SPINUP, 0);
if (err_mask && id[2] != 0x738c) {
rc = -EIO;
reason = "SPINUP failed";
goto err_out;
}
/*
* If the drive initially returned incomplete IDENTIFY info,
* we now must reissue the IDENTIFY command.
*/
if (id[2] == 0x37c8)
goto retry;
}
if ((flags & ATA_READID_POSTRESET) &&
(class == ATA_DEV_ATA || class == ATA_DEV_ZAC)) {
/*
* The exact sequence expected by certain pre-ATA4 drives is:
* SRST RESET
* IDENTIFY (optional in early ATA)
* INITIALIZE DEVICE PARAMETERS (later IDE and ATA)
* anything else..
* Some drives were very specific about that exact sequence.
*
* Note that ATA4 says lba is mandatory so the second check
* should never trigger.
*/
if (ata_id_major_version(id) < 4 || !ata_id_has_lba(id)) {
err_mask = ata_dev_init_params(dev, id[3], id[6]);
if (err_mask) {
rc = -EIO;
reason = "INIT_DEV_PARAMS failed";
goto err_out;
}
/* current CHS translation info (id[53-58]) might be
* changed. reread the identify device info.
*/
flags &= ~ATA_READID_POSTRESET;
goto retry;
}
}
*p_class = class;
return 0;
err_out:
if (ata_msg_warn(ap))
ata_dev_warn(dev, "failed to IDENTIFY (%s, err_mask=0x%x)\n",
reason, err_mask);
return rc;
}
static int ata_do_link_spd_horkage(struct ata_device *dev)
{
struct ata_link *plink = ata_dev_phys_link(dev);
u32 target, target_limit;
if (!sata_scr_valid(plink))
return 0;
if (dev->horkage & ATA_HORKAGE_1_5_GBPS)
target = 1;
else
return 0;
target_limit = (1 << target) - 1;
/* if already on stricter limit, no need to push further */
if (plink->sata_spd_limit <= target_limit)
return 0;
plink->sata_spd_limit = target_limit;
/* Request another EH round by returning -EAGAIN if link is
* going faster than the target speed. Forward progress is
* guaranteed by setting sata_spd_limit to target_limit above.
*/
if (plink->sata_spd > target) {
ata_dev_info(dev, "applying link speed limit horkage to %s\n",
sata_spd_string(target));
return -EAGAIN;
}
return 0;
}
static inline u8 ata_dev_knobble(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
if (ata_dev_blacklisted(dev) & ATA_HORKAGE_BRIDGE_OK)
return 0;
return ((ap->cbl == ATA_CBL_SATA) && (!ata_id_is_sata(dev->id)));
}
static int ata_dev_config_ncq(struct ata_device *dev,
char *desc, size_t desc_sz)
{
struct ata_port *ap = dev->link->ap;
int hdepth = 0, ddepth = ata_id_queue_depth(dev->id);
unsigned int err_mask;
char *aa_desc = "";
if (!ata_id_has_ncq(dev->id)) {
desc[0] = '\0';
return 0;
}
if (dev->horkage & ATA_HORKAGE_NONCQ) {
snprintf(desc, desc_sz, "NCQ (not used)");
return 0;
}
if (ap->flags & ATA_FLAG_NCQ) {
hdepth = min(ap->scsi_host->can_queue, ATA_MAX_QUEUE - 1);
dev->flags |= ATA_DFLAG_NCQ;
}
if (!(dev->horkage & ATA_HORKAGE_BROKEN_FPDMA_AA) &&
(ap->flags & ATA_FLAG_FPDMA_AA) &&
ata_id_has_fpdma_aa(dev->id)) {
err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_ENABLE,
SATA_FPDMA_AA);
if (err_mask) {
ata_dev_err(dev,
"failed to enable AA (error_mask=0x%x)\n",
err_mask);
if (err_mask != AC_ERR_DEV) {
dev->horkage |= ATA_HORKAGE_BROKEN_FPDMA_AA;
return -EIO;
}
} else
aa_desc = ", AA";
}
if (hdepth >= ddepth)
snprintf(desc, desc_sz, "NCQ (depth %d)%s", ddepth, aa_desc);
else
snprintf(desc, desc_sz, "NCQ (depth %d/%d)%s", hdepth,
ddepth, aa_desc);
if ((ap->flags & ATA_FLAG_FPDMA_AUX) &&
ata_id_has_ncq_send_and_recv(dev->id)) {
err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_SEND_RECV,
0, ap->sector_buf, 1);
if (err_mask) {
ata_dev_dbg(dev,
"failed to get NCQ Send/Recv Log Emask 0x%x\n",
err_mask);
} else {
u8 *cmds = dev->ncq_send_recv_cmds;
dev->flags |= ATA_DFLAG_NCQ_SEND_RECV;
memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_SEND_RECV_SIZE);
if (dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM) {
ata_dev_dbg(dev, "disabling queued TRIM support\n");
cmds[ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET] &=
~ATA_LOG_NCQ_SEND_RECV_DSM_TRIM;
}
}
}
return 0;
}
/**
* ata_dev_configure - Configure the specified ATA/ATAPI device
* @dev: Target device to configure
*
* Configure @dev according to @dev->id. Generic and low-level
* driver specific fixups are also applied.
*
* LOCKING:
* Kernel thread context (may sleep)
*
* RETURNS:
* 0 on success, -errno otherwise
*/
int ata_dev_configure(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
struct ata_eh_context *ehc = &dev->link->eh_context;
int print_info = ehc->i.flags & ATA_EHI_PRINTINFO;
const u16 *id = dev->id;
unsigned long xfer_mask;
unsigned int err_mask;
char revbuf[7]; /* XYZ-99\0 */
char fwrevbuf[ATA_ID_FW_REV_LEN+1];
char modelbuf[ATA_ID_PROD_LEN+1];
int rc;
if (!ata_dev_enabled(dev) && ata_msg_info(ap)) {
ata_dev_info(dev, "%s: ENTER/EXIT -- nodev\n", __func__);
return 0;
}
if (ata_msg_probe(ap))
ata_dev_dbg(dev, "%s: ENTER\n", __func__);
/* set horkage */
dev->horkage |= ata_dev_blacklisted(dev);
ata_force_horkage(dev);
if (dev->horkage & ATA_HORKAGE_DISABLE) {
ata_dev_info(dev, "unsupported device, disabling\n");
ata_dev_disable(dev);
return 0;
}
if ((!atapi_enabled || (ap->flags & ATA_FLAG_NO_ATAPI)) &&
dev->class == ATA_DEV_ATAPI) {
ata_dev_warn(dev, "WARNING: ATAPI is %s, device ignored\n",
atapi_enabled ? "not supported with this driver"
: "disabled");
ata_dev_disable(dev);
return 0;
}
rc = ata_do_link_spd_horkage(dev);
if (rc)
return rc;
/* some WD SATA-1 drives have issues with LPM, turn on NOLPM for them */
if ((dev->horkage & ATA_HORKAGE_WD_BROKEN_LPM) &&
(id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2)
dev->horkage |= ATA_HORKAGE_NOLPM;
if (dev->horkage & ATA_HORKAGE_NOLPM) {
ata_dev_warn(dev, "LPM support broken, forcing max_power\n");
dev->link->ap->target_lpm_policy = ATA_LPM_MAX_POWER;
}
/* let ACPI work its magic */
rc = ata_acpi_on_devcfg(dev);
if (rc)
return rc;
/* massage HPA, do it early as it might change IDENTIFY data */
rc = ata_hpa_resize(dev);
if (rc)
return rc;
/* print device capabilities */
if (ata_msg_probe(ap))
ata_dev_dbg(dev,
"%s: cfg 49:%04x 82:%04x 83:%04x 84:%04x "
"85:%04x 86:%04x 87:%04x 88:%04x\n",
__func__,
id[49], id[82], id[83], id[84],
id[85], id[86], id[87], id[88]);
/* initialize to-be-configured parameters */
dev->flags &= ~ATA_DFLAG_CFG_MASK;
dev->max_sectors = 0;
dev->cdb_len = 0;
dev->n_sectors = 0;
dev->cylinders = 0;
dev->heads = 0;
dev->sectors = 0;
dev->multi_count = 0;
/*
* common ATA, ATAPI feature tests
*/
/* find max transfer mode; for printk only */
xfer_mask = ata_id_xfermask(id);
if (ata_msg_probe(ap))
ata_dump_id(id);
/* SCSI only uses 4-char revisions, dump full 8 chars from ATA */
ata_id_c_string(dev->id, fwrevbuf, ATA_ID_FW_REV,
sizeof(fwrevbuf));
ata_id_c_string(dev->id, modelbuf, ATA_ID_PROD,
sizeof(modelbuf));
/* ATA-specific feature tests */
if (dev->class == ATA_DEV_ATA || dev->class == ATA_DEV_ZAC) {
if (ata_id_is_cfa(id)) {
/* CPRM may make this media unusable */
if (id[ATA_ID_CFA_KEY_MGMT] & 1)
ata_dev_warn(dev,
"supports DRM functions and may not be fully accessible\n");
snprintf(revbuf, 7, "CFA");
} else {
snprintf(revbuf, 7, "ATA-%d", ata_id_major_version(id));
/* Warn the user if the device has TPM extensions */
if (ata_id_has_tpm(id))
ata_dev_warn(dev,
"supports DRM functions and may not be fully accessible\n");
}
dev->n_sectors = ata_id_n_sectors(id);
/* get current R/W Multiple count setting */
if ((dev->id[47] >> 8) == 0x80 && (dev->id[59] & 0x100)) {
unsigned int max = dev->id[47] & 0xff;
unsigned int cnt = dev->id[59] & 0xff;
/* only recognize/allow powers of two here */
if (is_power_of_2(max) && is_power_of_2(cnt))
if (cnt <= max)
dev->multi_count = cnt;
}
if (ata_id_has_lba(id)) {
const char *lba_desc;
char ncq_desc[24];
lba_desc = "LBA";
dev->flags |= ATA_DFLAG_LBA;
if (ata_id_has_lba48(id)) {
dev->flags |= ATA_DFLAG_LBA48;
lba_desc = "LBA48";
if (dev->n_sectors >= (1UL << 28) &&
ata_id_has_flush_ext(id))
dev->flags |= ATA_DFLAG_FLUSH_EXT;
}
/* config NCQ */
rc = ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc));
if (rc)
return rc;
/* print device info to dmesg */
if (ata_msg_drv(ap) && print_info) {
ata_dev_info(dev, "%s: %s, %s, max %s\n",
revbuf, modelbuf, fwrevbuf,
ata_mode_string(xfer_mask));
ata_dev_info(dev,
"%llu sectors, multi %u: %s %s\n",
(unsigned long long)dev->n_sectors,
dev->multi_count, lba_desc, ncq_desc);
}
} else {
/* CHS */
/* Default translation */
dev->cylinders = id[1];
dev->heads = id[3];
dev->sectors = id[6];
if (ata_id_current_chs_valid(id)) {
/* Current CHS translation is valid. */
dev->cylinders = id[54];
dev->heads = id[55];
dev->sectors = id[56];
}
/* print device info to dmesg */
if (ata_msg_drv(ap) && print_info) {
ata_dev_info(dev, "%s: %s, %s, max %s\n",
revbuf, modelbuf, fwrevbuf,
ata_mode_string(xfer_mask));
ata_dev_info(dev,
"%llu sectors, multi %u, CHS %u/%u/%u\n",
(unsigned long long)dev->n_sectors,
dev->multi_count, dev->cylinders,
dev->heads, dev->sectors);
}
}
/* Check and mark DevSlp capability. Get DevSlp timing variables
* from SATA Settings page of Identify Device Data Log.
*/
if (ata_id_has_devslp(dev->id)) {
u8 *sata_setting = ap->sector_buf;
int i, j;
dev->flags |= ATA_DFLAG_DEVSLP;
err_mask = ata_read_log_page(dev,
ATA_LOG_SATA_ID_DEV_DATA,
ATA_LOG_SATA_SETTINGS,
sata_setting,
1);
if (err_mask)
ata_dev_dbg(dev,
"failed to get Identify Device Data, Emask 0x%x\n",
err_mask);
else
for (i = 0; i < ATA_LOG_DEVSLP_SIZE; i++) {
j = ATA_LOG_DEVSLP_OFFSET + i;
dev->devslp_timing[i] = sata_setting[j];
}
}
dev->cdb_len = 16;
}
/* ATAPI-specific feature tests */
else if (dev->class == ATA_DEV_ATAPI) {
const char *cdb_intr_string = "";
const char *atapi_an_string = "";
const char *dma_dir_string = "";
u32 sntf;
rc = atapi_cdb_len(id);
if ((rc < 12) || (rc > ATAPI_CDB_LEN)) {
if (ata_msg_warn(ap))
ata_dev_warn(dev, "unsupported CDB len\n");
rc = -EINVAL;
goto err_out_nosup;
}
dev->cdb_len = (unsigned int) rc;
/* Enable ATAPI AN if both the host and device have
* the support. If PMP is attached, SNTF is required
* to enable ATAPI AN to discern between PHY status
* changed notifications and ATAPI ANs.
*/
if (atapi_an &&
(ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id) &&
(!sata_pmp_attached(ap) ||
sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf) == 0)) {
/* issue SET feature command to turn this on */
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_ENABLE, SATA_AN);
if (err_mask)
ata_dev_err(dev,
"failed to enable ATAPI AN (err_mask=0x%x)\n",
err_mask);
else {
dev->flags |= ATA_DFLAG_AN;
atapi_an_string = ", ATAPI AN";
}
}
if (ata_id_cdb_intr(dev->id)) {
dev->flags |= ATA_DFLAG_CDB_INTR;
cdb_intr_string = ", CDB intr";
}
if (atapi_dmadir || (dev->horkage & ATA_HORKAGE_ATAPI_DMADIR) || atapi_id_dmadir(dev->id)) {
dev->flags |= ATA_DFLAG_DMADIR;
dma_dir_string = ", DMADIR";
}
if (ata_id_has_da(dev->id)) {
dev->flags |= ATA_DFLAG_DA;
zpodd_init(dev);
}
/* print device info to dmesg */
if (ata_msg_drv(ap) && print_info)
ata_dev_info(dev,
"ATAPI: %s, %s, max %s%s%s%s\n",
modelbuf, fwrevbuf,
ata_mode_string(xfer_mask),
cdb_intr_string, atapi_an_string,
dma_dir_string);
}
/* determine max_sectors */
dev->max_sectors = ATA_MAX_SECTORS;
if (dev->flags & ATA_DFLAG_LBA48)
dev->max_sectors = ATA_MAX_SECTORS_LBA48;
/* Limit PATA drive on SATA cable bridge transfers to udma5,
200 sectors */
if (ata_dev_knobble(dev)) {
if (ata_msg_drv(ap) && print_info)
ata_dev_info(dev, "applying bridge limits\n");
dev->udma_mask &= ATA_UDMA5;
dev->max_sectors = ATA_MAX_SECTORS;
}
if ((dev->class == ATA_DEV_ATAPI) &&
(atapi_command_packet_set(id) == TYPE_TAPE)) {
dev->max_sectors = ATA_MAX_SECTORS_TAPE;
dev->horkage |= ATA_HORKAGE_STUCK_ERR;
}
if (dev->horkage & ATA_HORKAGE_MAX_SEC_128)
dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128,
dev->max_sectors);
if (dev->horkage & ATA_HORKAGE_MAX_SEC_LBA48)
dev->max_sectors = ATA_MAX_SECTORS_LBA48;
if (ap->ops->dev_config)
ap->ops->dev_config(dev);
if (dev->horkage & ATA_HORKAGE_DIAGNOSTIC) {
/* Let the user know. We don't want to disallow opens for
rescue purposes, or in case the vendor is just a blithering
idiot. Do this after the dev_config call as some controllers
with buggy firmware may want to avoid reporting false device
bugs */
if (print_info) {
ata_dev_warn(dev,
"Drive reports diagnostics failure. This may indicate a drive\n");
ata_dev_warn(dev,
"fault or invalid emulation. Contact drive vendor for information.\n");
}
}
if ((dev->horkage & ATA_HORKAGE_FIRMWARE_WARN) && print_info) {
ata_dev_warn(dev, "WARNING: device requires firmware update to be fully functional\n");
ata_dev_warn(dev, " contact the vendor or visit http://ata.wiki.kernel.org\n");
}
return 0;
err_out_nosup:
if (ata_msg_probe(ap))
ata_dev_dbg(dev, "%s: EXIT, err\n", __func__);
return rc;
}
/**
* ata_cable_40wire - return 40 wire cable type
* @ap: port
*
* Helper method for drivers which want to hardwire 40 wire cable
* detection.
*/
int ata_cable_40wire(struct ata_port *ap)
{
return ATA_CBL_PATA40;
}
/**
* ata_cable_80wire - return 80 wire cable type
* @ap: port
*
* Helper method for drivers which want to hardwire 80 wire cable
* detection.
*/
int ata_cable_80wire(struct ata_port *ap)
{
return ATA_CBL_PATA80;
}
/**
* ata_cable_unknown - return unknown PATA cable.
* @ap: port
*
* Helper method for drivers which have no PATA cable detection.
*/
int ata_cable_unknown(struct ata_port *ap)
{
return ATA_CBL_PATA_UNK;
}
/**
* ata_cable_ignore - return ignored PATA cable.
* @ap: port
*
* Helper method for drivers which don't use cable type to limit
* transfer mode.
*/
int ata_cable_ignore(struct ata_port *ap)
{
return ATA_CBL_PATA_IGN;
}
/**
* ata_cable_sata - return SATA cable type
* @ap: port
*
* Helper method for drivers which have SATA cables
*/
int ata_cable_sata(struct ata_port *ap)
{
return ATA_CBL_SATA;
}
/**
* ata_bus_probe - Reset and probe ATA bus
* @ap: Bus to probe
*
* Master ATA bus probing function. Initiates a hardware-dependent
* bus reset, then attempts to identify any devices found on
* the bus.
*
* LOCKING:
* PCI/etc. bus probe sem.
*
* RETURNS:
* Zero on success, negative errno otherwise.
*/
int ata_bus_probe(struct ata_port *ap)
{
unsigned int classes[ATA_MAX_DEVICES];
int tries[ATA_MAX_DEVICES];
int rc;
struct ata_device *dev;
ata_for_each_dev(dev, &ap->link, ALL)
tries[dev->devno] = ATA_PROBE_MAX_TRIES;
retry:
ata_for_each_dev(dev, &ap->link, ALL) {
/* If we issue an SRST then an ATA drive (not ATAPI)
* may change configuration and be in PIO0 timing. If
* we do a hard reset (or are coming from power on)
* this is true for ATA or ATAPI. Until we've set a
* suitable controller mode we should not touch the
* bus as we may be talking too fast.
*/
dev->pio_mode = XFER_PIO_0;
dev->dma_mode = 0xff;
/* If the controller has a pio mode setup function
* then use it to set the chipset to rights. Don't
* touch the DMA setup as that will be dealt with when
* configuring devices.
*/
if (ap->ops->set_piomode)
ap->ops->set_piomode(ap, dev);
}
/* reset and determine device classes */
ap->ops->phy_reset(ap);
ata_for_each_dev(dev, &ap->link, ALL) {
if (dev->class != ATA_DEV_UNKNOWN)
classes[dev->devno] = dev->class;
else
classes[dev->devno] = ATA_DEV_NONE;
dev->class = ATA_DEV_UNKNOWN;
}
/* read IDENTIFY page and configure devices. We have to do the identify
specific sequence bass-ackwards so that PDIAG- is released by
the slave device */
ata_for_each_dev(dev, &ap->link, ALL_REVERSE) {
if (tries[dev->devno])
dev->class = classes[dev->devno];
if (!ata_dev_enabled(dev))
continue;
rc = ata_dev_read_id(dev, &dev->class, ATA_READID_POSTRESET,
dev->id);
if (rc)
goto fail;
}
/* Now ask for the cable type as PDIAG- should have been released */
if (ap->ops->cable_detect)
ap->cbl = ap->ops->cable_detect(ap);
/* We may have SATA bridge glue hiding here irrespective of
* the reported cable types and sensed types. When SATA
* drives indicate we have a bridge, we don't know which end
* of the link the bridge is which is a problem.
*/
ata_for_each_dev(dev, &ap->link, ENABLED)
if (ata_id_is_sata(dev->id))
ap->cbl = ATA_CBL_SATA;
/* After the identify sequence we can now set up the devices. We do
this in the normal order so that the user doesn't get confused */
ata_for_each_dev(dev, &ap->link, ENABLED) {
ap->link.eh_context.i.flags |= ATA_EHI_PRINTINFO;
rc = ata_dev_configure(dev);
ap->link.eh_context.i.flags &= ~ATA_EHI_PRINTINFO;
if (rc)
goto fail;
}
/* configure transfer mode */
rc = ata_set_mode(&ap->link, &dev);
if (rc)
goto fail;
ata_for_each_dev(dev, &ap->link, ENABLED)
return 0;
return -ENODEV;
fail:
tries[dev->devno]--;
switch (rc) {
case -EINVAL:
/* eeek, something went very wrong, give up */
tries[dev->devno] = 0;
break;
case -ENODEV:
/* give it just one more chance */
tries[dev->devno] = min(tries[dev->devno], 1);
case -EIO:
if (tries[dev->devno] == 1) {
/* This is the last chance, better to slow
* down than lose it.
*/
sata_down_spd_limit(&ap->link, 0);
ata_down_xfermask_limit(dev, ATA_DNXFER_PIO);
}
}
if (!tries[dev->devno])
ata_dev_disable(dev);
goto retry;
}
/**
* sata_print_link_status - Print SATA link status
* @link: SATA link to printk link status about
*
* This function prints link speed and status of a SATA link.
*
* LOCKING:
* None.
*/
static void sata_print_link_status(struct ata_link *link)
{
u32 sstatus, scontrol, tmp;
if (sata_scr_read(link, SCR_STATUS, &sstatus))
return;
sata_scr_read(link, SCR_CONTROL, &scontrol);
if (ata_phys_link_online(link)) {
tmp = (sstatus >> 4) & 0xf;
ata_link_info(link, "SATA link up %s (SStatus %X SControl %X)\n",
sata_spd_string(tmp), sstatus, scontrol);
} else {
ata_link_info(link, "SATA link down (SStatus %X SControl %X)\n",
sstatus, scontrol);
}
}
/**
* ata_dev_pair - return other device on cable
* @adev: device
*
* Obtain the other device on the same cable, or if none is
* present NULL is returned
*/
struct ata_device *ata_dev_pair(struct ata_device *adev)
{
struct ata_link *link = adev->link;
struct ata_device *pair = &link->device[1 - adev->devno];
if (!ata_dev_enabled(pair))
return NULL;
return pair;
}
/**
* sata_down_spd_limit - adjust SATA spd limit downward
* @link: Link to adjust SATA spd limit for
* @spd_limit: Additional limit
*
* Adjust SATA spd limit of @link downward. Note that this
* function only adjusts the limit. The change must be applied
* using sata_set_spd().
*
* If @spd_limit is non-zero, the speed is limited to equal to or
* lower than @spd_limit if such speed is supported. If
* @spd_limit is slower than any supported speed, only the lowest
* supported speed is allowed.
*
* LOCKING:
* Inherited from caller.
*
* RETURNS:
* 0 on success, negative errno on failure
*/
int sata_down_spd_limit(struct ata_link *link, u32 spd_limit)
{
u32 sstatus, spd, mask;
int rc, bit;
if (!sata_scr_valid(link))
return -EOPNOTSUPP;
/* If SCR can be read, use it to determine the current SPD.
* If not, use cached value in link->sata_spd.
*/
rc = sata_scr_read(link, SCR_STATUS, &sstatus);
if (rc == 0 && ata_sstatus_online(sstatus))
spd = (sstatus >> 4) & 0xf;
else
spd = link->sata_spd;
mask = link->sata_spd_limit;
if (mask <= 1)
return -EINVAL;
/* unconditionally mask off the highest bit */
bit = fls(mask) - 1;
mask &= ~(1 << bit);
/* Mask off all speeds higher than or equal to the current
* one. Force 1.5Gbps if current SPD is not available.
*/
if (spd > 1)
mask &= (1 << (spd - 1)) - 1;
else
mask &= 1;
/* were we already at the bottom? */
if (!mask)
return -EINVAL;
if (spd_limit) {
if (mask & ((1 << spd_limit) - 1))
mask &= (1 << spd_limit) - 1;
else {
bit = ffs(mask) - 1;
mask = 1 << bit;
}
}
link->sata_spd_limit = mask;
ata_link_warn(link, "limiting SATA link speed to %s\n",
sata_spd_string(fls(mask)));
return 0;
}
static int __sata_set_spd_needed(struct ata_link *link, u32 *scontrol)
{
struct ata_link *host_link = &link->ap->link;
u32 limit, target, spd;
limit = link->sata_spd_limit;
/* Don't configure downstream link faster than upstream link.
* It doesn't speed up anything and some PMPs choke on such
* configuration.
*/
if (!ata_is_host_link(link) && host_link->sata_spd)
limit &= (1 << host_link->sata_spd) - 1;
if (limit == UINT_MAX)
target = 0;
else
target = fls(limit);
spd = (*scontrol >> 4) & 0xf;
*scontrol = (*scontrol & ~0xf0) | ((target & 0xf) << 4);
return spd != target;
}
/**
* sata_set_spd_needed - is SATA spd configuration needed
* @link: Link in question
*
* Test whether the spd limit in SControl matches
* @link->sata_spd_limit. This function is used to determine
* whether hardreset is necessary to apply SATA spd
* configuration.
*
* LOCKING:
* Inherited from caller.
*
* RETURNS:
* 1 if SATA spd configuration is needed, 0 otherwise.
*/
static int sata_set_spd_needed(struct ata_link *link)
{
u32 scontrol;
if (sata_scr_read(link, SCR_CONTROL, &scontrol))
return 1;
return __sata_set_spd_needed(link, &scontrol);
}
/**
* sata_set_spd - set SATA spd according to spd limit
* @link: Link to set SATA spd for
*
* Set SATA spd of @link according to sata_spd_limit.
*
* LOCKING:
* Inherited from caller.
*
* RETURNS:
* 0 if spd doesn't need to be changed, 1 if spd has been
* changed. Negative errno if SCR registers are inaccessible.
*/
int sata_set_spd(struct ata_link *link)
{
u32 scontrol;
int rc;
if ((rc = sata_scr_read(link, SCR_CONTROL, &scontrol)))
return rc;
if (!__sata_set_spd_needed(link, &scontrol))
return 0;
if ((rc = sata_scr_write(link, SCR_CONTROL, scontrol)))
return rc;
return 1;
}
/*
* This mode timing computation functionality is ported over from
* drivers/ide/ide-timing.h and was originally written by Vojtech Pavlik
*/
/*
* PIO 0-4, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds).
* These were taken from ATA/ATAPI-6 standard, rev 0a, except
* for UDMA6, which is currently supported only by Maxtor drives.
*
* For PIO 5/6 MWDMA 3/4 see the CFA specification 3.0.
*/
static const struct ata_timing ata_timing[] = {
/* { XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 0, 960, 0 }, */
{ XFER_PIO_0, 70, 290, 240, 600, 165, 150, 0, 600, 0 },
{ XFER_PIO_1, 50, 290, 93, 383, 125, 100, 0, 383, 0 },
{ XFER_PIO_2, 30, 290, 40, 330, 100, 90, 0, 240, 0 },
{ XFER_PIO_3, 30, 80, 70, 180, 80, 70, 0, 180, 0 },
{ XFER_PIO_4, 25, 70, 25, 120, 70, 25, 0, 120, 0 },
{ XFER_PIO_5, 15, 65, 25, 100, 65, 25, 0, 100, 0 },
{ XFER_PIO_6, 10, 55, 20, 80, 55, 20, 0, 80, 0 },
{ XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 50, 960, 0 },
{ XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 30, 480, 0 },
{ XFER_SW_DMA_2, 60, 0, 0, 0, 120, 120, 20, 240, 0 },
{ XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 20, 480, 0 },
{ XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 5, 150, 0 },
{ XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 5, 120, 0 },
{ XFER_MW_DMA_3, 25, 0, 0, 0, 65, 25, 5, 100, 0 },
{ XFER_MW_DMA_4, 25, 0, 0, 0, 55, 20, 5, 80, 0 },
/* { XFER_UDMA_SLOW, 0, 0, 0, 0, 0, 0, 0, 0, 150 }, */
{ XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 0, 120 },
{ XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 0, 80 },
{ XFER_UDMA_2, 0, 0, 0, 0, 0, 0, 0, 0, 60 },
{ XFER_UDMA_3, 0, 0, 0, 0, 0, 0, 0, 0, 45 },
{ XFER_UDMA_4, 0, 0, 0, 0, 0, 0, 0, 0, 30 },
{ XFER_UDMA_5, 0, 0, 0, 0, 0, 0, 0, 0, 20 },
{ XFER_UDMA_6, 0, 0, 0, 0, 0, 0, 0, 0, 15 },
{ 0xFF }
};
#define ENOUGH(v, unit) (((v)-1)/(unit)+1)
#define EZ(v, unit) ((v)?ENOUGH(v, unit):0)
static void ata_timing_quantize(const struct ata_timing *t, struct ata_timing *q, int T, int UT)
{
q->setup = EZ(t->setup * 1000, T);
q->act8b = EZ(t->act8b * 1000, T);
q->rec8b = EZ(t->rec8b * 1000, T);
q->cyc8b = EZ(t->cyc8b * 1000, T);
q->active = EZ(t->active * 1000, T);
q->recover = EZ(t->recover * 1000, T);
q->dmack_hold = EZ(t->dmack_hold * 1000, T);
q->cycle = EZ(t->cycle * 1000, T);
q->udma = EZ(t->udma * 1000, UT);
}
void ata_timing_merge(const struct ata_timing *a, const struct ata_timing *b,
struct ata_timing *m, unsigned int what)
{
if (what & ATA_TIMING_SETUP ) m->setup = max(a->setup, b->setup);
if (what & ATA_TIMING_ACT8B ) m->act8b = max(a->act8b, b->act8b);
if (what & ATA_TIMING_REC8B ) m->rec8b = max(a->rec8b, b->rec8b);
if (what & ATA_TIMING_CYC8B ) m->cyc8b = max(a->cyc8b, b->cyc8b);
if (what & ATA_TIMING_ACTIVE ) m->active = max(a->active, b->active);
if (what & ATA_TIMING_RECOVER) m->recover = max(a->recover, b->recover);
if (what & ATA_TIMING_DMACK_HOLD) m->dmack_hold = max(a->dmack_hold, b->dmack_hold);
if (what & ATA_TIMING_CYCLE ) m->cycle = max(a->cycle, b->cycle);
if (what & ATA_TIMING_UDMA ) m->udma = max(a->udma, b->udma);
}
const struct ata_timing *ata_timing_find_mode(u8 xfer_mode)
{
const struct ata_timing *t = ata_timing;
while (xfer_mode > t->mode)
t++;
if (xfer_mode == t->mode)
return t;
WARN_ONCE(true, "%s: unable to find timing for xfer_mode 0x%x\n",
__func__, xfer_mode);
return NULL;
}
int ata_timing_compute(struct ata_device *adev, unsigned short speed,
struct ata_timing *t, int T, int UT)
{
const u16 *id = adev->id;
const struct ata_timing *s;
struct ata_timing p;
/*
* Find the mode.
*/
if (!(s = ata_timing_find_mode(speed)))
return -EINVAL;
memcpy(t, s, sizeof(*s));
/*
* If the drive is an EIDE drive, it can tell us it needs extended
* PIO/MW_DMA cycle timing.
*/
if (id[ATA_ID_FIELD_VALID] & 2) { /* EIDE drive */
memset(&p, 0, sizeof(p));
if (speed >= XFER_PIO_0 && speed < XFER_SW_DMA_0) {
if (speed <= XFER_PIO_2)
p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO];
else if ((speed <= XFER_PIO_4) ||
(speed == XFER_PIO_5 && !ata_id_is_cfa(id)))
p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO_IORDY];
} else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2)
p.cycle = id[ATA_ID_EIDE_DMA_MIN];
ata_timing_merge(&p, t, t, ATA_TIMING_CYCLE | ATA_TIMING_CYC8B);
}
/*
* Convert the timing to bus clock counts.
*/
ata_timing_quantize(t, t, T, UT);
/*
* Even in DMA/UDMA modes we still use PIO access for IDENTIFY,
* S.M.A.R.T * and some other commands. We have to ensure that the
* DMA cycle timing is slower/equal than the fastest PIO timing.
*/
if (speed > XFER_PIO_6) {
ata_timing_compute(adev, adev->pio_mode, &p, T, UT);
ata_timing_merge(&p, t, t, ATA_TIMING_ALL);
}
/*
* Lengthen active & recovery time so that cycle time is correct.
*/
if (t->act8b + t->rec8b < t->cyc8b) {
t->act8b += (t->cyc8b - (t->act8b + t->rec8b)) / 2;
t->rec8b = t->cyc8b - t->act8b;
}
if (t->active + t->recover < t->cycle) {
t->active += (t->cycle - (t->active + t->recover)) / 2;
t->recover = t->cycle - t->active;
}
/* In a few cases quantisation may produce enough errors to
leave t->cycle too low for the sum of active and recovery
if so we must correct this */
if (t->active + t->recover > t->cycle)
t->cycle = t->active + t->recover;
return 0;
}
/**
* ata_timing_cycle2mode - find xfer mode for the specified cycle duration
* @xfer_shift: ATA_SHIFT_* value for transfer type to examine.
* @cycle: cycle duration in ns
*
* Return matching xfer mode for @cycle. The returned mode is of
* the transfer type specified by @xfer_shift. If @cycle is too
* slow for @xfer_shift, 0xff is returned. If @cycle is faster
* than the fastest known mode, the fasted mode is returned.
*
* LOCKING:
* None.
*
* RETURNS:
* Matching xfer_mode, 0xff if no match found.
*/
u8 ata_timing_cycle2mode(unsigned int xfer_shift, int cycle)
{
u8 base_mode = 0xff, last_mode = 0xff;
const struct ata_xfer_ent *ent;
const struct ata_timing *t;
for (ent = ata_xfer_tbl; ent->shift >= 0; ent++)
if (ent->shift == xfer_shift)
base_mode = ent->base;
for (t = ata_timing_find_mode(base_mode);
t && ata_xfer_mode2shift(t->mode) == xfer_shift; t++) {
unsigned short this_cycle;
switch (xfer_shift) {
case ATA_SHIFT_PIO:
case ATA_SHIFT_MWDMA:
this_cycle = t->cycle;
break;
case ATA_SHIFT_UDMA:
this_cycle = t->udma;
break;
default:
return 0xff;
}
if (cycle > this_cycle)
break;
last_mode = t->mode;
}
return last_mode;
}
/**
* ata_down_xfermask_limit - adjust dev xfer masks downward
* @dev: Device to adjust xfer masks
* @sel: ATA_DNXFER_* selector
*
* Adjust xfer masks of @dev downward. Note that this function
* does not apply the change. Invoking ata_set_mode() afterwards
* will apply the limit.
*
* LOCKING:
* Inherited from caller.
*
* RETURNS:
* 0 on success, negative errno on failure
*/
int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel)
{
char buf[32];
unsigned long orig_mask, xfer_mask;
unsigned long pio_mask, mwdma_mask, udma_mask;
int quiet, highbit;
quiet = !!(sel & ATA_DNXFER_QUIET);
sel &= ~ATA_DNXFER_QUIET;
xfer_mask = orig_mask = ata_pack_xfermask(dev->pio_mask,
dev->mwdma_mask,
dev->udma_mask);
ata_unpack_xfermask(xfer_mask, &pio_mask, &mwdma_mask, &udma_mask);
switch (sel) {
case ATA_DNXFER_PIO:
highbit = fls(pio_mask) - 1;
pio_mask &= ~(1 << highbit);
break;
case ATA_DNXFER_DMA:
if (udma_mask) {
highbit = fls(udma_mask) - 1;
udma_mask &= ~(1 << highbit);
if (!udma_mask)
return -ENOENT;
} else if (mwdma_mask) {
highbit = fls(mwdma_mask) - 1;
mwdma_mask &= ~(1 << highbit);
if (!mwdma_mask)
return -ENOENT;
}
break;
case ATA_DNXFER_40C:
udma_mask &= ATA_UDMA_MASK_40C;
break;
case ATA_DNXFER_FORCE_PIO0:
pio_mask &= 1;
case ATA_DNXFER_FORCE_PIO:
mwdma_mask = 0;
udma_mask = 0;
break;
default:
BUG();
}
xfer_mask &= ata_pack_xfermask(pio_mask, mwdma_mask, udma_mask);
if (!(xfer_mask & ATA_MASK_PIO) || xfer_mask == orig_mask)
return -ENOENT;
if (!quiet) {
if (xfer_mask & (ATA_MASK_MWDMA | ATA_MASK_UDMA))
snprintf(buf, sizeof(buf), "%s:%s",
ata_mode_string(xfer_mask),
ata_mode_string(xfer_mask & ATA_MASK_PIO));
else
snprintf(buf, sizeof(buf), "%s",
ata_mode_string(xfer_mask));
ata_dev_warn(dev, "limiting speed to %s\n", buf);
}
ata_unpack_xfermask(xfer_mask, &dev->pio_mask, &dev->mwdma_mask,
&dev->udma_mask);
return 0;
}
static int ata_dev_set_mode(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
struct ata_eh_context *ehc = &dev->link->eh_context;
const bool nosetxfer = dev->horkage & ATA_HORKAGE_NOSETXFER;
const char *dev_err_whine = "";
int ign_dev_err = 0;
unsigned int err_mask = 0;
int rc;
dev->flags &= ~ATA_DFLAG_PIO;
if (dev->xfer_shift == ATA_SHIFT_PIO)
dev->flags |= ATA_DFLAG_PIO;
if (nosetxfer && ap->flags & ATA_FLAG_SATA && ata_id_is_sata(dev->id))
dev_err_whine = " (SET_XFERMODE skipped)";
else {
if (nosetxfer)
ata_dev_warn(dev,
"NOSETXFER but PATA detected - can't "
"skip SETXFER, might malfunction\n");
err_mask = ata_dev_set_xfermode(dev);
}
if (err_mask & ~AC_ERR_DEV)
goto fail;
/* revalidate */
ehc->i.flags |= ATA_EHI_POST_SETMODE;
rc = ata_dev_revalidate(dev, ATA_DEV_UNKNOWN, 0);
ehc->i.flags &= ~ATA_EHI_POST_SETMODE;
if (rc)
return rc;
if (dev->xfer_shift == ATA_SHIFT_PIO) {
/* Old CFA may refuse this command, which is just fine */
if (ata_id_is_cfa(dev->id))
ign_dev_err = 1;
/* Catch several broken garbage emulations plus some pre
ATA devices */
if (ata_id_major_version(dev->id) == 0 &&
dev->pio_mode <= XFER_PIO_2)
ign_dev_err = 1;
/* Some very old devices and some bad newer ones fail
any kind of SET_XFERMODE request but support PIO0-2
timings and no IORDY */
if (!ata_id_has_iordy(dev->id) && dev->pio_mode <= XFER_PIO_2)
ign_dev_err = 1;
}
/* Early MWDMA devices do DMA but don't allow DMA mode setting.
Don't fail an MWDMA0 set IFF the device indicates it is in MWDMA0 */
if (dev->xfer_shift == ATA_SHIFT_MWDMA &&
dev->dma_mode == XFER_MW_DMA_0 &&
(dev->id[63] >> 8) & 1)
ign_dev_err = 1;
/* if the device is actually configured correctly, ignore dev err */
if (dev->xfer_mode == ata_xfer_mask2mode(ata_id_xfermask(dev->id)))
ign_dev_err = 1;
if (err_mask & AC_ERR_DEV) {
if (!ign_dev_err)
goto fail;
else
dev_err_whine = " (device error ignored)";
}
DPRINTK("xfer_shift=%u, xfer_mode=0x%x\n",
dev->xfer_shift, (int)dev->xfer_mode);
ata_dev_info(dev, "configured for %s%s\n",
ata_mode_string(ata_xfer_mode2mask(dev->xfer_mode)),
dev_err_whine);
return 0;
fail:
ata_dev_err(dev, "failed to set xfermode (err_mask=0x%x)\n", err_mask);
return -EIO;
}
/**
* ata_do_set_mode - Program timings and issue SET FEATURES - XFER
* @link: link on which timings will be programmed
* @r_failed_dev: out parameter for failed device
*
* Standard implementation of the function used to tune and set
* ATA device disk transfer mode (PIO3, UDMA6, etc.). If
* ata_dev_set_mode() fails, pointer to the failing device is
* returned in @r_failed_dev.
*
* LOCKING:
* PCI/etc. bus probe sem.
*
* RETURNS:
* 0 on success, negative errno otherwise
*/
int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
{
struct ata_port *ap = link->ap;
struct ata_device *dev;
int rc = 0, used_dma = 0, found = 0;
/* step 1: calculate xfer_mask */
ata_for_each_dev(dev, link, ENABLED) {
unsigned long pio_mask, dma_mask;
unsigned int mode_mask;
mode_mask = ATA_DMA_MASK_ATA;
if (dev->class == ATA_DEV_ATAPI)
mode_mask = ATA_DMA_MASK_ATAPI;
else if (ata_id_is_cfa(dev->id))
mode_mask = ATA_DMA_MASK_CFA;
ata_dev_xfermask(dev);
ata_force_xfermask(dev);
pio_mask = ata_pack_xfermask(dev->pio_mask, 0, 0);
if (libata_dma_mask & mode_mask)
dma_mask = ata_pack_xfermask(0, dev->mwdma_mask,
dev->udma_mask);
else
dma_mask = 0;
dev->pio_mode = ata_xfer_mask2mode(pio_mask);
dev->dma_mode = ata_xfer_mask2mode(dma_mask);
found = 1;
if (ata_dma_enabled(dev))
used_dma = 1;
}
if (!found)
goto out;
/* step 2: always set host PIO timings */
ata_for_each_dev(dev, link, ENABLED) {
if (dev->pio_mode == 0xff) {
ata_dev_warn(dev, "no PIO support\n");
rc = -EINVAL;
goto out;
}
dev->xfer_mode = dev->pio_mode;
dev->xfer_shift = ATA_SHIFT_PIO;
if (ap->ops->set_piomode)
ap->ops->set_piomode(ap, dev);
}
/* step 3: set host DMA timings */
ata_for_each_dev(dev, link, ENABLED) {
if (!ata_dma_enabled(dev))
continue;
dev->xfer_mode = dev->dma_mode;
dev->xfer_shift = ata_xfer_mode2shift(dev->dma_mode);
if (ap->ops->set_dmamode)
ap->ops->set_dmamode(ap, dev);
}
/* step 4: update devices' xfer mode */
ata_for_each_dev(dev, link, ENABLED) {
rc = ata_dev_set_mode(dev);
if (rc)
goto out;
}
/* Record simplex status. If we selected DMA then the other
* host channels are not permitted to do so.
*/
if (used_dma && (ap->host->flags & ATA_HOST_SIMPLEX))
ap->host->simplex_claimed = ap;
out:
if (rc)
*r_failed_dev = dev;
return rc;
}
/**
* ata_wait_ready - wait for link to become ready
* @link: link to be waited on
* @deadline: deadline jiffies for the operation
* @check_ready: callback to check link readiness
*
* Wait for @link to become ready. @check_ready should return
* positive number if @link is ready, 0 if it isn't, -ENODEV if
* link doesn't seem to be occupied, other errno for other error
* conditions.
*
* Transient -ENODEV conditions are allowed for
* ATA_TMOUT_FF_WAIT.
*
* LOCKING:
* EH context.
*
* RETURNS:
* 0 if @linke is ready before @deadline; otherwise, -errno.
*/
int ata_wait_ready(struct ata_link *link, unsigned long deadline,
int (*check_ready)(struct ata_link *link))
{
unsigned long start = jiffies;
unsigned long nodev_deadline;
int warned = 0;
/* choose which 0xff timeout to use, read comment in libata.h */
if (link->ap->host->flags & ATA_HOST_PARALLEL_SCAN)
nodev_deadline = ata_deadline(start, ATA_TMOUT_FF_WAIT_LONG);
else
nodev_deadline = ata_deadline(start, ATA_TMOUT_FF_WAIT);
/* Slave readiness can't be tested separately from master. On
* M/S emulation configuration, this function should be called
* only on the master and it will handle both master and slave.
*/
WARN_ON(link == link->ap->slave_link);
if (time_after(nodev_deadline, deadline))
nodev_deadline = deadline;
while (1) {
unsigned long now = jiffies;
int ready, tmp;
ready = tmp = check_ready(link);
if (ready > 0)
return 0;
/*
* -ENODEV could be transient. Ignore -ENODEV if link
* is online. Also, some SATA devices take a long
* time to clear 0xff after reset. Wait for
* ATA_TMOUT_FF_WAIT[_LONG] on -ENODEV if link isn't
* offline.
*
* Note that some PATA controllers (pata_ali) explode
* if status register is read more than once when
* there's no device attached.
*/
if (ready == -ENODEV) {
if (ata_link_online(link))
ready = 0;
else if ((link->ap->flags & ATA_FLAG_SATA) &&
!ata_link_offline(link) &&
time_before(now, nodev_deadline))
ready = 0;
}
if (ready)
return ready;
if (time_after(now, deadline))
return -EBUSY;
if (!warned && time_after(now, start + 5 * HZ) &&
(deadline - now > 3 * HZ)) {
ata_link_warn(link,
"link is slow to respond, please be patient "
"(ready=%d)\n", tmp);
warned = 1;
}
ata_msleep(link->ap, 50);
}
}
/**
* ata_wait_after_reset - wait for link to become ready after reset
* @link: link to be waited on
* @deadline: deadline jiffies for the operation
* @check_ready: callback to check link readiness
*
* Wait for @link to become ready after reset.
*
* LOCKING:
* EH context.
*
* RETURNS:
* 0 if @linke is ready before @deadline; otherwise, -errno.
*/
int ata_wait_after_reset(struct ata_link *link, unsigned long deadline,
int (*check_ready)(struct ata_link *link))
{
ata_msleep(link->ap, ATA_WAIT_AFTER_RESET);
return ata_wait_ready(link, deadline, check_ready);
}
/**
* sata_link_debounce - debounce SATA phy status
* @link: ATA link to debounce SATA phy status for
* @params: timing parameters { interval, duratinon, timeout } in msec
* @deadline: deadline jiffies for the operation
*
* Make sure SStatus of @link reaches stable state, determined by
* holding the same value where DET is not 1 for @duration polled
* every @interval, before @timeout. Timeout constraints the
* beginning of the stable state. Because DET gets stuck at 1 on
* some controllers after hot unplugging, this functions waits
* until timeout then returns 0 if DET is stable at 1.
*
* @timeout is further limited by @deadline. The sooner of the
* two is used.
*
* LOCKING:
* Kernel thread context (may sleep)
*
* RETURNS:
* 0 on success, -errno on failure.
*/
int sata_link_debounce(struct ata_link *link, const unsigned long *params,
unsigned long deadline)
{
unsigned long interval = params[0];
unsigned long duration = params[1];
unsigned long last_jiffies, t;
u32 last, cur;
int rc;
t = ata_deadline(jiffies, params[2]);
if (time_before(t, deadline))
deadline = t;
if ((rc = sata_scr_read(link, SCR_STATUS, &cur)))
return rc;
cur &= 0xf;
last = cur;
last_jiffies = jiffies;
while (1) {
ata_msleep(link->ap, interval);
if ((rc = sata_scr_read(link, SCR_STATUS, &cur)))
return rc;
cur &= 0xf;
/* DET stable? */
if (cur == last) {
if (cur == 1 && time_before(jiffies, deadline))
continue;
if (time_after(jiffies,
ata_deadline(last_jiffies, duration)))
return 0;
continue;
}
/* unstable, start over */
last = cur;
last_jiffies = jiffies;
/* Check deadline. If debouncing failed, return
* -EPIPE to tell upper layer to lower link speed.
*/
if (time_after(jiffies, deadline))
return -EPIPE;
}
}
/**
* sata_link_resume - resume SATA link
* @link: ATA link to resume SATA
* @params: timing parameters { interval, duratinon, timeout } in msec
* @deadline: deadline jiffies for the operation
*
* Resume SATA phy @link and debounce it.
*
* LOCKING:
* Kernel thread context (may sleep)
*
* RETURNS:
* 0 on success, -errno on failure.
*/
int sata_link_resume(struct ata_link *link, const unsigned long *params,
unsigned long deadline)
{
int tries = ATA_LINK_RESUME_TRIES;
u32 scontrol, serror;
int rc;
if ((rc = sata_scr_read(link, SCR_CONTROL, &scontrol)))
return rc;
/*
* Writes to SControl sometimes get ignored under certain
* controllers (ata_piix SIDPR). Make sure DET actually is
* cleared.
*/
do {
scontrol = (scontrol & 0x0f0) | 0x300;
if ((rc = sata_scr_write(link, SCR_CONTROL, scontrol)))
return rc;
/*
* Some PHYs react badly if SStatus is pounded
* immediately after resuming. Delay 200ms before
* debouncing.
*/
ata_msleep(link->ap, 200);
/* is SControl restored correctly? */
if ((rc = sata_scr_read(link, SCR_CONTROL, &scontrol)))
return rc;
} while ((scontrol & 0xf0f) != 0x300 && --tries);
if ((scontrol & 0xf0f) != 0x300) {
ata_link_warn(link, "failed to resume link (SControl %X)\n",
scontrol);
return 0;
}
if (tries < ATA_LINK_RESUME_TRIES)
ata_link_warn(link, "link resume succeeded after %d retries\n",
ATA_LINK_RESUME_TRIES - tries);
if ((rc = sata_link_debounce(link, params, deadline)))
return rc;
/* clear SError, some PHYs require this even for SRST to work */
if (!(rc = sata_scr_read(link, SCR_ERROR, &serror)))
rc = sata_scr_write(link, SCR_ERROR, serror);
return rc != -EINVAL ? rc : 0;
}
/**
* sata_link_scr_lpm - manipulate SControl IPM and SPM fields
* @link: ATA link to manipulate SControl for
* @policy: LPM policy to configure
* @spm_wakeup: initiate LPM transition to active state
*
* Manipulate the IPM field of the SControl register of @link
* according to @policy. If @policy is ATA_LPM_MAX_POWER and
* @spm_wakeup is %true, the SPM field is manipulated to wake up
* the link. This function also clears PHYRDY_CHG before
* returning.
*
* LOCKING:
* EH context.
*
* RETURNS:
* 0 on succes, -errno otherwise.
*/
int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
bool spm_wakeup)
{
struct ata_eh_context *ehc = &link->eh_context;
bool woken_up = false;
u32 scontrol;
int rc;
rc = sata_scr_read(link, SCR_CONTROL, &scontrol);
if (rc)
return rc;
switch (policy) {
case ATA_LPM_MAX_POWER:
/* disable all LPM transitions */
scontrol |= (0x7 << 8);
/* initiate transition to active state */
if (spm_wakeup) {
scontrol |= (0x4 << 12);
woken_up = true;
}
break;
case ATA_LPM_MED_POWER:
/* allow LPM to PARTIAL */
scontrol &= ~(0x1 << 8);
scontrol |= (0x6 << 8);
break;
case ATA_LPM_MIN_POWER:
if (ata_link_nr_enabled(link) > 0)
/* no restrictions on LPM transitions */
scontrol &= ~(0x7 << 8);
else {
/* empty port, power off */
scontrol &= ~0xf;
scontrol |= (0x1 << 2);
}
break;
default:
WARN_ON(1);
}
rc = sata_scr_write(link, SCR_CONTROL, scontrol);
if (rc)
return rc;
/* give the link time to transit out of LPM state */
if (woken_up)
msleep(10);
/* clear PHYRDY_CHG from SError */
ehc->i.serror &= ~SERR_PHYRDY_CHG;
return sata_scr_write(link, SCR_ERROR, SERR_PHYRDY_CHG);
}
/**
* ata_std_prereset - prepare for reset
* @link: ATA link to be reset
* @deadline: deadline jiffies for the operation
*
* @link is about to be reset. Initialize it. Failure from
* prereset makes libata abort whole reset sequence and give up
* that port, so prereset should be best-effort. It does its
* best to prepare for reset sequence but if things go wrong, it
* should just whine, not fail.
*
* LOCKING:
* Kernel thread context (may sleep)
*
* RETURNS:
* 0 on success, -errno otherwise.
*/
int ata_std_prereset(struct ata_link *link, unsigned long deadline)
{
struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
const unsigned long *timing = sata_ehc_deb_timing(ehc);
int rc;
/* if we're about to do hardreset, nothing more to do */
if (ehc->i.action & ATA_EH_HARDRESET)
return 0;
/* if SATA, resume link */
if (ap->flags & ATA_FLAG_SATA) {
rc = sata_link_resume(link, timing, deadline);
/* whine about phy resume failure but proceed */
if (rc && rc != -EOPNOTSUPP)
ata_link_warn(link,
"failed to resume link for reset (errno=%d)\n",
rc);
}
/* no point in trying softreset on offline link */
if (ata_phys_link_offline(link))
ehc->i.action &= ~ATA_EH_SOFTRESET;
return 0;
}
/**
* sata_link_hardreset - reset link via SATA phy reset
* @link: link to reset
* @timing: timing parameters { interval, duratinon, timeout } in msec
* @deadline: deadline jiffies for the operation
* @online: optional out parameter indicating link onlineness
* @check_ready: optional callback to check link readiness
*
* SATA phy-reset @link using DET bits of SControl register.
* After hardreset, link readiness is waited upon using
* ata_wait_ready() if @check_ready is specified. LLDs are
* allowed to not specify @check_ready and wait itself after this
* function returns. Device classification is LLD's
* responsibility.
*
* *@online is set to one iff reset succeeded and @link is online
* after reset.
*
* LOCKING:
* Kernel thread context (may sleep)
*
* RETURNS:
* 0 on success, -errno otherwise.
*/
int sata_link_hardreset(struct ata_link *link, const unsigned long *timing,
unsigned long deadline,
bool *online, int (*check_ready)(struct ata_link *))
{
u32 scontrol;
int rc;
DPRINTK("ENTER\n");
if (online)
*online = false;
if (sata_set_spd_needed(link)) {
/* SATA spec says nothing about how to reconfigure
* spd. To be on the safe side, turn off phy during
* reconfiguration. This works for at least ICH7 AHCI
* and Sil3124.
*/
if ((rc = sata_scr_read(link, SCR_CONTROL, &scontrol)))
goto out;
scontrol = (scontrol & 0x0f0) | 0x304;
if ((rc = sata_scr_write(link, SCR_CONTROL, scontrol)))
goto out;
sata_set_spd(link);
}
/* issue phy wake/reset */
if ((rc = sata_scr_read(link, SCR_CONTROL, &scontrol)))
goto out;
scontrol = (scontrol & 0x0f0) | 0x301;
if ((rc = sata_scr_write_flush(link, SCR_CONTROL, scontrol)))
goto out;
/* Couldn't find anything in SATA I/II specs, but AHCI-1.1
* 10.4.2 says at least 1 ms.
*/
ata_msleep(link->ap, 1);
/* bring link back */
rc = sata_link_resume(link, timing, deadline);
if (rc)
goto out;
/* if link is offline nothing more to do */
if (ata_phys_link_offline(link))
goto out;
/* Link is online. From this point, -ENODEV too is an error. */
if (online)
*online = true;
if (sata_pmp_supported(link->ap) && ata_is_host_link(link)) {
/* If PMP is supported, we have to do follow-up SRST.
* Some PMPs don't send D2H Reg FIS after hardreset if
* the first port is empty. Wait only for
* ATA_TMOUT_PMP_SRST_WAIT.
*/
if (check_ready) {
unsigned long pmp_deadline;
pmp_deadline = ata_deadline(jiffies,
ATA_TMOUT_PMP_SRST_WAIT);
if (time_after(pmp_deadline, deadline))
pmp_deadline = deadline;
ata_wait_ready(link, pmp_deadline, check_ready);
}
rc = -EAGAIN;
goto out;
}
rc = 0;
if (check_ready)
rc = ata_wait_ready(link, deadline, check_ready);
out:
if (rc && rc != -EAGAIN) {
/* online is set iff link is online && reset succeeded */
if (online)
*online = false;
ata_link_err(link, "COMRESET failed (errno=%d)\n", rc);
}
DPRINTK("EXIT, rc=%d\n", rc);
return rc;
}
/**
* sata_std_hardreset - COMRESET w/o waiting or classification
* @link: link to reset
* @class: resulting class of attached device
* @deadline: deadline jiffies for the operation
*
* Standard SATA COMRESET w/o waiting or classification.
*
* LOCKING:
* Kernel thread context (may sleep)
*
* RETURNS:
* 0 if link offline, -EAGAIN if link online, -errno on errors.
*/
int sata_std_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
bool online;
int rc;
/* do hardreset */
rc = sata_link_hardreset(link, timing, deadline, &online, NULL);
return online ? -EAGAIN : rc;
}
/**
* ata_std_postreset - standard postreset callback
* @link: the target ata_link
* @classes: classes of attached devices
*
* This function is invoked after a successful reset. Note that
* the device might have been reset more than once using
* different reset methods before postreset is invoked.
*
* LOCKING:
* Kernel thread context (may sleep)
*/
void ata_std_postreset(struct ata_link *link, unsigned int *classes)
{
u32 serror;
DPRINTK("ENTER\n");
/* reset complete, clear SError */
if (!sata_scr_read(link, SCR_ERROR, &serror))
sata_scr_write(link, SCR_ERROR, serror);
/* print link status */
sata_print_link_status(link);
DPRINTK("EXIT\n");
}
/**
* ata_dev_same_device - Determine whether new ID matches configured device
* @dev: device to compare against
* @new_class: class of the new device
* @new_id: IDENTIFY page of the new device
*
* Compare @new_class and @new_id against @dev and determine
* whether @dev is the device indicated by @new_class and
* @new_id.
*
* LOCKING:
* None.
*
* RETURNS:
* 1 if @dev matches @new_class and @new_id, 0 otherwise.
*/
static int ata_dev_same_device(struct ata_device *dev, unsigned int new_class,
const u16 *new_id)
{
const u16 *old_id = dev->id;
unsigned char model[2][ATA_ID_PROD_LEN + 1];
unsigned char serial[2][ATA_ID_SERNO_LEN + 1];
if (dev->class != new_class) {
ata_dev_info(dev, "class mismatch %d != %d\n",
dev->class, new_class);
return 0;
}
ata_id_c_string(old_id, model[0], ATA_ID_PROD, sizeof(model[0]));
ata_id_c_string(new_id, model[1], ATA_ID_PROD, sizeof(model[1]));
ata_id_c_string(old_id, serial[0], ATA_ID_SERNO, sizeof(serial[0]));
ata_id_c_string(new_id, serial[1], ATA_ID_SERNO, sizeof(serial[1]));
if (strcmp(model[0], model[1])) {
ata_dev_info(dev, "model number mismatch '%s' != '%s'\n",
model[0], model[1]);
return 0;
}
if (strcmp(serial[0], serial[1])) {
ata_dev_info(dev, "serial number mismatch '%s' != '%s'\n",
serial[0], serial[1]);
return 0;
}
return 1;
}
/**
* ata_dev_reread_id - Re-read IDENTIFY data
* @dev: target ATA device
* @readid_flags: read ID flags
*
* Re-read IDENTIFY page and make sure @dev is still attached to
* the port.
*
* LOCKING:
* Kernel thread context (may sleep)
*
* RETURNS:
* 0 on success, negative errno otherwise
*/
int ata_dev_reread_id(struct ata_device *dev, unsigned int readid_flags)
{
unsigned int class = dev->class;
u16 *id = (void *)dev->link->ap->sector_buf;
int rc;
/* read ID data */
rc = ata_dev_read_id(dev, &class, readid_flags, id);
if (rc)
return rc;
/* is the device still there? */
if (!ata_dev_same_device(dev, class, id))
return -ENODEV;
memcpy(dev->id, id, sizeof(id[0]) * ATA_ID_WORDS);
return 0;
}
/**
* ata_dev_revalidate - Revalidate ATA device
* @dev: device to revalidate
* @new_class: new class code
* @readid_flags: read ID flags
*
* Re-read IDENTIFY page, make sure @dev is still attached to the
* port and reconfigure it according to the new IDENTIFY page.
*
* LOCKING:
* Kernel thread context (may sleep)
*
* RETURNS:
* 0 on success, negative errno otherwise
*/
int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
unsigned int readid_flags)
{
u64 n_sectors = dev->n_sectors;
u64 n_native_sectors = dev->n_native_sectors;
int rc;
if (!ata_dev_enabled(dev))
return -ENODEV;
/* fail early if !ATA && !ATAPI to avoid issuing [P]IDENTIFY to PMP */
if (ata_class_enabled(new_class) &&
new_class != ATA_DEV_ATA &&
new_class != ATA_DEV_ATAPI &&
new_class != ATA_DEV_ZAC &&
new_class != ATA_DEV_SEMB) {
ata_dev_info(dev, "class mismatch %u != %u\n",
dev->class, new_class);
rc = -ENODEV;
goto fail;
}
/* re-read ID */
rc = ata_dev_reread_id(dev, readid_flags);
if (rc)
goto fail;
/* configure device according to the new ID */
rc = ata_dev_configure(dev);
if (rc)
goto fail;
/* verify n_sectors hasn't changed */
if (dev->class != ATA_DEV_ATA || !n_sectors ||
dev->n_sectors == n_sectors)
return 0;
/* n_sectors has changed */
ata_dev_warn(dev, "n_sectors mismatch %llu != %llu\n",
(unsigned long long)n_sectors,
(unsigned long long)dev->n_sectors);
/*
* Something could have caused HPA to be unlocked
* involuntarily. If n_native_sectors hasn't changed and the
* new size matches it, keep the device.
*/
if (dev->n_native_sectors == n_native_sectors &&
dev->n_sectors > n_sectors && dev->n_sectors == n_native_sectors) {
ata_dev_warn(dev,
"new n_sectors matches native, probably "
"late HPA unlock, n_sectors updated\n");
/* use the larger n_sectors */
return 0;
}
/*
* Some BIOSes boot w/o HPA but resume w/ HPA locked. Try
* unlocking HPA in those cases.
*
* https://bugzilla.kernel.org/show_bug.cgi?id=15396
*/
if (dev->n_native_sectors == n_native_sectors &&
dev->n_sectors < n_sectors && n_sectors == n_native_sectors &&
!(dev->horkage & ATA_HORKAGE_BROKEN_HPA)) {
ata_dev_warn(dev,
"old n_sectors matches native, probably "
"late HPA lock, will try to unlock HPA\n");
/* try unlocking HPA */
dev->flags |= ATA_DFLAG_UNLOCK_HPA;
rc = -EIO;
} else
rc = -ENODEV;
/* restore original n_[native_]sectors and fail */
dev->n_native_sectors = n_native_sectors;
dev->n_sectors = n_sectors;
fail:
ata_dev_err(dev, "revalidation failed (errno=%d)\n", rc);
return rc;
}
struct ata_blacklist_entry {
const char *model_num;
const char *model_rev;
unsigned long horkage;
};
static const struct ata_blacklist_entry ata_device_blacklist [] = {
/* Devices with DMA related problems under Linux */
{ "WDC AC11000H", NULL, ATA_HORKAGE_NODMA },
{ "WDC AC22100H", NULL, ATA_HORKAGE_NODMA },
{ "WDC AC32500H", NULL, ATA_HORKAGE_NODMA },
{ "WDC AC33100H", NULL, ATA_HORKAGE_NODMA },
{ "WDC AC31600H", NULL, ATA_HORKAGE_NODMA },
{ "WDC AC32100H", "24.09P07", ATA_HORKAGE_NODMA },
{ "WDC AC23200L", "21.10N21", ATA_HORKAGE_NODMA },
{ "Compaq CRD-8241B", NULL, ATA_HORKAGE_NODMA },
{ "CRD-8400B", NULL, ATA_HORKAGE_NODMA },
{ "CRD-848[02]B", NULL, ATA_HORKAGE_NODMA },
{ "CRD-84", NULL, ATA_HORKAGE_NODMA },
{ "SanDisk SDP3B", NULL, ATA_HORKAGE_NODMA },
{ "SanDisk SDP3B-64", NULL, ATA_HORKAGE_NODMA },
{ "SANYO CD-ROM CRD", NULL, ATA_HORKAGE_NODMA },
{ "HITACHI CDR-8", NULL, ATA_HORKAGE_NODMA },
{ "HITACHI CDR-8[34]35",NULL, ATA_HORKAGE_NODMA },
{ "Toshiba CD-ROM XM-6202B", NULL, ATA_HORKAGE_NODMA },
{ "TOSHIBA CD-ROM XM-1702BC", NULL, ATA_HORKAGE_NODMA },
{ "CD-532E-A", NULL, ATA_HORKAGE_NODMA },
{ "E-IDE CD-ROM CR-840",NULL, ATA_HORKAGE_NODMA },
{ "CD-ROM Drive/F5A", NULL, ATA_HORKAGE_NODMA },
{ "WPI CDD-820", NULL, ATA_HORKAGE_NODMA },
{ "SAMSUNG CD-ROM SC-148C", NULL, ATA_HORKAGE_NODMA },
{ "SAMSUNG CD-ROM SC", NULL, ATA_HORKAGE_NODMA },
{ "ATAPI CD-ROM DRIVE 40X MAXIMUM",NULL,ATA_HORKAGE_NODMA },
{ "_NEC DV5800A", NULL, ATA_HORKAGE_NODMA },
{ "SAMSUNG CD-ROM SN-124", "N001", ATA_HORKAGE_NODMA },
{ "Seagate STT20000A", NULL, ATA_HORKAGE_NODMA },
{ " 2GB ATA Flash Disk", "ADMA428M", ATA_HORKAGE_NODMA },
/* Odd clown on sil3726/4726 PMPs */
{ "Config Disk", NULL, ATA_HORKAGE_DISABLE },
/* Weird ATAPI devices */
{ "TORiSAN DVD-ROM DRD-N216", NULL, ATA_HORKAGE_MAX_SEC_128 },
{ "QUANTUM DAT DAT72-000", NULL, ATA_HORKAGE_ATAPI_MOD16_DMA },
{ "Slimtype DVD A DS8A8SH", NULL, ATA_HORKAGE_MAX_SEC_LBA48 },
{ "Slimtype DVD A DS8A9SH", NULL, ATA_HORKAGE_MAX_SEC_LBA48 },
/* Devices we expect to fail diagnostics */
/* Devices where NCQ should be avoided */
/* NCQ is slow */
{ "WDC WD740ADFD-00", NULL, ATA_HORKAGE_NONCQ },
{ "WDC WD740ADFD-00NLR1", NULL, ATA_HORKAGE_NONCQ, },
/* http://thread.gmane.org/gmane.linux.ide/14907 */
{ "FUJITSU MHT2060BH", NULL, ATA_HORKAGE_NONCQ },
/* NCQ is broken */
{ "Maxtor *", "BANC*", ATA_HORKAGE_NONCQ },
{ "Maxtor 7V300F0", "VA111630", ATA_HORKAGE_NONCQ },
{ "ST380817AS", "3.42", ATA_HORKAGE_NONCQ },
{ "ST3160023AS", "3.42", ATA_HORKAGE_NONCQ },
{ "OCZ CORE_SSD", "02.10104", ATA_HORKAGE_NONCQ },
/* Seagate NCQ + FLUSH CACHE firmware bug */
{ "ST31500341AS", "SD1[5-9]", ATA_HORKAGE_NONCQ |
ATA_HORKAGE_FIRMWARE_WARN },
{ "ST31000333AS", "SD1[5-9]", ATA_HORKAGE_NONCQ |
ATA_HORKAGE_FIRMWARE_WARN },
{ "ST3640[36]23AS", "SD1[5-9]", ATA_HORKAGE_NONCQ |
ATA_HORKAGE_FIRMWARE_WARN },
{ "ST3320[68]13AS", "SD1[5-9]", ATA_HORKAGE_NONCQ |
ATA_HORKAGE_FIRMWARE_WARN },
/* Seagate Momentus SpinPoint M8 seem to have FPMDA_AA issues */
{ "ST1000LM024 HN-M101MBB", "2AR10001", ATA_HORKAGE_BROKEN_FPDMA_AA },
{ "ST1000LM024 HN-M101MBB", "2BA30001", ATA_HORKAGE_BROKEN_FPDMA_AA },
/* Blacklist entries taken from Silicon Image 3124/3132
Windows driver .inf file - also several Linux problem reports */
{ "HTS541060G9SA00", "MB3OC60D", ATA_HORKAGE_NONCQ, },
{ "HTS541080G9SA00", "MB4OC60D", ATA_HORKAGE_NONCQ, },
{ "HTS541010G9SA00", "MBZOC60D", ATA_HORKAGE_NONCQ, },
/* https://bugzilla.kernel.org/show_bug.cgi?id=15573 */
{ "C300-CTFDDAC128MAG", "0001", ATA_HORKAGE_NONCQ, },
/* devices which puke on READ_NATIVE_MAX */
{ "HDS724040KLSA80", "KFAOA20N", ATA_HORKAGE_BROKEN_HPA, },
{ "WDC WD3200JD-00KLB0", "WD-WCAMR1130137", ATA_HORKAGE_BROKEN_HPA },
{ "WDC WD2500JD-00HBB0", "WD-WMAL71490727", ATA_HORKAGE_BROKEN_HPA },
{ "MAXTOR 6L080L4", "A93.0500", ATA_HORKAGE_BROKEN_HPA },
/* this one allows HPA unlocking but fails IOs on the area */
{ "OCZ-VERTEX", "1.30", ATA_HORKAGE_BROKEN_HPA },
/* Devices which report 1 sector over size HPA */
{ "ST340823A", NULL, ATA_HORKAGE_HPA_SIZE, },
{ "ST320413A", NULL, ATA_HORKAGE_HPA_SIZE, },
{ "ST310211A", NULL, ATA_HORKAGE_HPA_SIZE, },
/* Devices which get the IVB wrong */
{ "QUANTUM FIREBALLlct10 05", "A03.0900", ATA_HORKAGE_IVB, },
/* Maybe we should just blacklist TSSTcorp... */
{ "TSSTcorp CDDVDW SH-S202[HJN]", "SB0[01]", ATA_HORKAGE_IVB, },
/* Devices that do not need bridging limits applied */
{ "MTRON MSP-SATA*", NULL, ATA_HORKAGE_BRIDGE_OK, },
{ "BUFFALO HD-QSU2/R5", NULL, ATA_HORKAGE_BRIDGE_OK, },
/* Devices which aren't very happy with higher link speeds */
{ "WD My Book", NULL, ATA_HORKAGE_1_5_GBPS, },
{ "Seagate FreeAgent GoFlex", NULL, ATA_HORKAGE_1_5_GBPS, },
/*
* Devices which choke on SETXFER. Applies only if both the
* device and controller are SATA.
*/
{ "PIONEER DVD-RW DVRTD08", NULL, ATA_HORKAGE_NOSETXFER },
{ "PIONEER DVD-RW DVRTD08A", NULL, ATA_HORKAGE_NOSETXFER },
{ "PIONEER DVD-RW DVR-215", NULL, ATA_HORKAGE_NOSETXFER },
{ "PIONEER DVD-RW DVR-212D", NULL, ATA_HORKAGE_NOSETXFER },
{ "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER },
/* devices that don't properly handle queued TRIM commands */
{ "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Micron_M5[15]0*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Crucial_CT*M550*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Crucial_CT*MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Samsung SSD 850 PRO*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
/*
* As defined, the DRAT (Deterministic Read After Trim) and RZAT
* (Return Zero After Trim) flags in the ATA Command Set are
* unreliable in the sense that they only define what happens if
* the device successfully executed the DSM TRIM command. TRIM
* is only advisory, however, and the device is free to silently
* ignore all or parts of the request.
*
* Whitelist drives that are known to reliably return zeroes
* after TRIM.
*/
/*
* The intel 510 drive has buggy DRAT/RZAT. Explicitly exclude
* that model before whitelisting all other intel SSDs.
*/
{ "INTEL*SSDSC2MH*", NULL, 0, },
{ "Micron*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Crucial*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "INTEL*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "SSD*INTEL*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Samsung*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "SAMSUNG*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "ST[1248][0248]0[FH]*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
/*
* Some WD SATA-I drives spin up and down erratically when the link
* is put into the slumber mode. We don't have full list of the
* affected devices. Disable LPM if the device matches one of the
* known prefixes and is SATA-1. As a side effect LPM partial is
* lost too.
*
* https://bugzilla.kernel.org/show_bug.cgi?id=57211
*/
{ "WDC WD800JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
{ "WDC WD1200JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
{ "WDC WD1600JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
{ "WDC WD2000JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
{ "WDC WD2500JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
{ "WDC WD3000JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
{ "WDC WD3200JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM },
/* End Marker */
{ }
};
static unsigned long ata_dev_blacklisted(const struct ata_device *dev)
{
unsigned char model_num[ATA_ID_PROD_LEN + 1];
unsigned char model_rev[ATA_ID_FW_REV_LEN + 1];
const struct ata_blacklist_entry *ad = ata_device_blacklist;
ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num));
ata_id_c_string(dev->id, model_rev, ATA_ID_FW_REV, sizeof(model_rev));
while (ad->model_num) {
if (glob_match(ad->model_num, model_num)) {
if (ad->model_rev == NULL)
return ad->horkage;
if (glob_match(ad->model_rev, model_rev))
return ad->horkage;
}
ad++;
}
return 0;
}
static int ata_dma_blacklisted(const struct ata_device *dev)
{
/* We don't support polling DMA.
* DMA blacklist those ATAPI devices with CDB-intr (and use PIO)
* if the LLDD handles only interrupts in the HSM_ST_LAST state.
*/
if ((dev->link->ap->flags & ATA_FLAG_PIO_POLLING) &&
(dev->flags & ATA_DFLAG_CDB_INTR))
return 1;
return (dev->horkage & ATA_HORKAGE_NODMA) ? 1 : 0;
}
/**
* ata_is_40wire - check drive side detection
* @dev: device
*
* Perform drive side detection decoding, allowing for device vendors
* who can't follow the documentation.
*/
static int ata_is_40wire(struct ata_device *dev)
{
if (dev->horkage & ATA_HORKAGE_IVB)
return ata_drive_40wire_relaxed(dev->id);
return ata_drive_40wire(dev->id);
}
/**
* cable_is_40wire - 40/80/SATA decider
* @ap: port to consider
*
* This function encapsulates the policy for speed management
* in one place. At the moment we don't cache the result but
* there is a good case for setting ap->cbl to the result when
* we are called with unknown cables (and figuring out if it
* impacts hotplug at all).
*
* Return 1 if the cable appears to be 40 wire.
*/
static int cable_is_40wire(struct ata_port *ap)
{
struct ata_link *link;
struct ata_device *dev;
/* If the controller thinks we are 40 wire, we are. */
if (ap->cbl == ATA_CBL_PATA40)
return 1;
/* If the controller thinks we are 80 wire, we are. */
if (ap->cbl == ATA_CBL_PATA80 || ap->cbl == ATA_CBL_SATA)
return 0;
/* If the system is known to be 40 wire short cable (eg
* laptop), then we allow 80 wire modes even if the drive
* isn't sure.
*/
if (ap->cbl == ATA_CBL_PATA40_SHORT)
return 0;
/* If the controller doesn't know, we scan.
*
* Note: We look for all 40 wire detects at this point. Any
* 80 wire detect is taken to be 80 wire cable because
* - in many setups only the one drive (slave if present) will
* give a valid detect
* - if you have a non detect capable drive you don't want it
* to colour the choice
*/
ata_for_each_link(link, ap, EDGE) {
ata_for_each_dev(dev, link, ENABLED) {
if (!ata_is_40wire(dev))
return 0;
}
}
return 1;
}
/**
* ata_dev_xfermask - Compute supported xfermask of the given device
* @dev: Device to compute xfermask for
*
* Compute supported xfermask of @dev and store it in
* dev->*_mask. This function is responsible for applying all
* known limits including host controller limits, device
* blacklist, etc...
*
* LOCKING:
* None.
*/
static void ata_dev_xfermask(struct ata_device *dev)
{
struct ata_link *link = dev->link;
struct ata_port *ap = link->ap;
struct ata_host *host = ap->host;
unsigned long xfer_mask;
/* controller modes available */
xfer_mask = ata_pack_xfermask(ap->pio_mask,
ap->mwdma_mask, ap->udma_mask);
/* drive modes available */
xfer_mask &= ata_pack_xfermask(dev->pio_mask,
dev->mwdma_mask, dev->udma_mask);
xfer_mask &= ata_id_xfermask(dev->id);
/*
* CFA Advanced TrueIDE timings are not allowed on a shared
* cable
*/
if (ata_dev_pair(dev)) {
/* No PIO5 or PIO6 */
xfer_mask &= ~(0x03 << (ATA_SHIFT_PIO + 5));
/* No MWDMA3 or MWDMA 4 */
xfer_mask &= ~(0x03 << (ATA_SHIFT_MWDMA + 3));
}
if (ata_dma_blacklisted(dev)) {
xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA);
ata_dev_warn(dev,
"device is on DMA blacklist, disabling DMA\n");
}
if ((host->flags & ATA_HOST_SIMPLEX) &&
host->simplex_claimed && host->simplex_claimed != ap) {
xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA);
ata_dev_warn(dev,
"simplex DMA is claimed by other device, disabling DMA\n");
}
if (ap->flags & ATA_FLAG_NO_IORDY)
xfer_mask &= ata_pio_mask_no_iordy(dev);
if (ap->ops->mode_filter)
xfer_mask = ap->ops->mode_filter(dev, xfer_mask);
/* Apply cable rule here. Don't apply it early because when
* we handle hot plug the cable type can itself change.
* Check this last so that we know if the transfer rate was
* solely limited by the cable.
* Unknown or 80 wire cables reported host side are checked
* drive side as well. Cases where we know a 40wire cable
* is used safely for 80 are not checked here.
*/
if (xfer_mask & (0xF8 << ATA_SHIFT_UDMA))
/* UDMA/44 or higher would be available */
if (cable_is_40wire(ap)) {
ata_dev_warn(dev,
"limited to UDMA/33 due to 40-wire cable\n");
xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA);
}
ata_unpack_xfermask(xfer_mask, &dev->pio_mask,
&dev->mwdma_mask, &dev->udma_mask);
}
/**
* ata_dev_set_xfermode - Issue SET FEATURES - XFER MODE command
* @dev: Device to which command will be sent
*
* Issue SET FEATURES - XFER MODE command to device @dev
* on port @ap.
*
* LOCKING:
* PCI/etc. bus probe sem.
*
* RETURNS:
* 0 on success, AC_ERR_* mask otherwise.
*/
static unsigned int ata_dev_set_xfermode(struct ata_device *dev)
{
struct ata_taskfile tf;
unsigned int err_mask;
/* set up set-features taskfile */
DPRINTK("set features - xfer mode\n");
/* Some controllers and ATAPI devices show flaky interrupt
* behavior after setting xfer mode. Use polling instead.
*/
ata_tf_init(dev, &tf);
tf.command = ATA_CMD_SET_FEATURES;
tf.feature = SETFEATURES_XFER;
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_POLLING;
tf.protocol = ATA_PROT_NODATA;
/* If we are using IORDY we must send the mode setting command */
if (ata_pio_need_iordy(dev))
tf.nsect = dev->xfer_mode;
/* If the device has IORDY and the controller does not - turn it off */
else if (ata_id_has_iordy(dev->id))
tf.nsect = 0x01;
else /* In the ancient relic department - skip all of this */
return 0;
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
DPRINTK("EXIT, err_mask=%x\n", err_mask);
return err_mask;
}
/**
* ata_dev_set_feature - Issue SET FEATURES - SATA FEATURES
* @dev: Device to which command will be sent
* @enable: Whether to enable or disable the feature
* @feature: The sector count represents the feature to set
*
* Issue SET FEATURES - SATA FEATURES command to device @dev
* on port @ap with sector count
*
* LOCKING:
* PCI/etc. bus probe sem.
*
* RETURNS:
* 0 on success, AC_ERR_* mask otherwise.
*/
unsigned int ata_dev_set_feature(struct ata_device *dev, u8 enable, u8 feature)
{
struct ata_taskfile tf;
unsigned int err_mask;
/* set up set-features taskfile */
DPRINTK("set features - SATA features\n");
ata_tf_init(dev, &tf);
tf.command = ATA_CMD_SET_FEATURES;
tf.feature = enable;
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf.protocol = ATA_PROT_NODATA;
tf.nsect = feature;
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
DPRINTK("EXIT, err_mask=%x\n", err_mask);
return err_mask;
}
EXPORT_SYMBOL_GPL(ata_dev_set_feature);
/**
* ata_dev_init_params - Issue INIT DEV PARAMS command
* @dev: Device to which command will be sent
* @heads: Number of heads (taskfile parameter)
* @sectors: Number of sectors (taskfile parameter)
*
* LOCKING:
* Kernel thread context (may sleep)
*
* RETURNS:
* 0 on success, AC_ERR_* mask otherwise.
*/
static unsigned int ata_dev_init_params(struct ata_device *dev,
u16 heads, u16 sectors)
{
struct ata_taskfile tf;
unsigned int err_mask;
/* Number of sectors per track 1-255. Number of heads 1-16 */
if (sectors < 1 || sectors > 255 || heads < 1 || heads > 16)
return AC_ERR_INVALID;
/* set up init dev params taskfile */
DPRINTK("init dev params \n");
ata_tf_init(dev, &tf);
tf.command = ATA_CMD_INIT_DEV_PARAMS;
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf.protocol = ATA_PROT_NODATA;
tf.nsect = sectors;
tf.device |= (heads - 1) & 0x0f; /* max head = num. of heads - 1 */
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
/* A clean abort indicates an original or just out of spec drive
and we should continue as we issue the setup based on the
drive reported working geometry */
if (err_mask == AC_ERR_DEV && (tf.feature & ATA_ABORTED))
err_mask = 0;
DPRINTK("EXIT, err_mask=%x\n", err_mask);
return err_mask;
}
/**
* ata_sg_clean - Unmap DMA memory associated with command
* @qc: Command containing DMA memory to be released
*
* Unmap all mapped DMA memory associated with this command.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
void ata_sg_clean(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct scatterlist *sg = qc->sg;
int dir = qc->dma_dir;
WARN_ON_ONCE(sg == NULL);
VPRINTK("unmapping %u sg elements\n", qc->n_elem);
if (qc->n_elem)
dma_unmap_sg(ap->dev, sg, qc->orig_n_elem, dir);
qc->flags &= ~ATA_QCFLAG_DMAMAP;
qc->sg = NULL;
}
/**
* atapi_check_dma - Check whether ATAPI DMA can be supported
* @qc: Metadata associated with taskfile to check
*
* Allow low-level driver to filter ATA PACKET commands, returning
* a status indicating whether or not it is OK to use DMA for the
* supplied PACKET command.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*
* RETURNS: 0 when ATAPI DMA can be used
* nonzero otherwise
*/
int atapi_check_dma(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
/* Don't allow DMA if it isn't multiple of 16 bytes. Quite a
* few ATAPI devices choke on such DMA requests.
*/
if (!(qc->dev->horkage & ATA_HORKAGE_ATAPI_MOD16_DMA) &&
unlikely(qc->nbytes & 15))
return 1;
if (ap->ops->check_atapi_dma)
return ap->ops->check_atapi_dma(qc);
return 0;
}
/**
* ata_std_qc_defer - Check whether a qc needs to be deferred
* @qc: ATA command in question
*
* Non-NCQ commands cannot run with any other command, NCQ or
* not. As upper layer only knows the queue depth, we are
* responsible for maintaining exclusion. This function checks
* whether a new command @qc can be issued.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*
* RETURNS:
* ATA_DEFER_* if deferring is needed, 0 otherwise.
*/
int ata_std_qc_defer(struct ata_queued_cmd *qc)
{
struct ata_link *link = qc->dev->link;
if (qc->tf.protocol == ATA_PROT_NCQ) {
if (!ata_tag_valid(link->active_tag))
return 0;
} else {
if (!ata_tag_valid(link->active_tag) && !link->sactive)
return 0;
}
return ATA_DEFER_LINK;
}
void ata_noop_qc_prep(struct ata_queued_cmd *qc) { }
/**
* ata_sg_init - Associate command with scatter-gather table.
* @qc: Command to be associated
* @sg: Scatter-gather table.
* @n_elem: Number of elements in s/g table.
*
* Initialize the data-related elements of queued_cmd @qc
* to point to a scatter-gather table @sg, containing @n_elem
* elements.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg,
unsigned int n_elem)
{
qc->sg = sg;
qc->n_elem = n_elem;
qc->cursg = qc->sg;
}
/**
* ata_sg_setup - DMA-map the scatter-gather table associated with a command.
* @qc: Command with scatter-gather table to be mapped.
*
* DMA-map the scatter-gather table associated with queued_cmd @qc.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*
* RETURNS:
* Zero on success, negative on error.
*
*/
static int ata_sg_setup(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
unsigned int n_elem;
VPRINTK("ENTER, ata%u\n", ap->print_id);
n_elem = dma_map_sg(ap->dev, qc->sg, qc->n_elem, qc->dma_dir);
if (n_elem < 1)
return -1;
DPRINTK("%d sg elements mapped\n", n_elem);
qc->orig_n_elem = qc->n_elem;
qc->n_elem = n_elem;
qc->flags |= ATA_QCFLAG_DMAMAP;
return 0;
}
/**
* swap_buf_le16 - swap halves of 16-bit words in place
* @buf: Buffer to swap
* @buf_words: Number of 16-bit words in buffer.
*
* Swap halves of 16-bit words if needed to convert from
* little-endian byte order to native cpu byte order, or
* vice-versa.
*
* LOCKING:
* Inherited from caller.
*/
void swap_buf_le16(u16 *buf, unsigned int buf_words)
{
#ifdef __BIG_ENDIAN
unsigned int i;
for (i = 0; i < buf_words; i++)
buf[i] = le16_to_cpu(buf[i]);
#endif /* __BIG_ENDIAN */
}
/**
* ata_qc_new_init - Request an available ATA command, and initialize it
* @dev: Device from whom we request an available command structure
*
* LOCKING:
* None.
*/
struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag)
{
struct ata_port *ap = dev->link->ap;
struct ata_queued_cmd *qc;
/* no command while frozen */
if (unlikely(ap->pflags & ATA_PFLAG_FROZEN))
return NULL;
/* libsas case */
if (ap->flags & ATA_FLAG_SAS_HOST) {
tag = ata_sas_allocate_tag(ap);
if (tag < 0)
return NULL;
}
qc = __ata_qc_from_tag(ap, tag);
qc->tag = tag;
qc->scsicmd = NULL;
qc->ap = ap;
qc->dev = dev;
ata_qc_reinit(qc);
return qc;
}
/**
* ata_qc_free - free unused ata_queued_cmd
* @qc: Command to complete
*
* Designed to free unused ata_queued_cmd object
* in case something prevents using it.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
void ata_qc_free(struct ata_queued_cmd *qc)
{
struct ata_port *ap;
unsigned int tag;
WARN_ON_ONCE(qc == NULL); /* ata_qc_from_tag _might_ return NULL */
ap = qc->ap;
qc->flags = 0;
tag = qc->tag;
if (likely(ata_tag_valid(tag))) {
qc->tag = ATA_TAG_POISON;
if (ap->flags & ATA_FLAG_SAS_HOST)
ata_sas_free_tag(tag, ap);
}
}
void __ata_qc_complete(struct ata_queued_cmd *qc)
{
struct ata_port *ap;
struct ata_link *link;
WARN_ON_ONCE(qc == NULL); /* ata_qc_from_tag _might_ return NULL */
WARN_ON_ONCE(!(qc->flags & ATA_QCFLAG_ACTIVE));
ap = qc->ap;
link = qc->dev->link;
if (likely(qc->flags & ATA_QCFLAG_DMAMAP))
ata_sg_clean(qc);
/* command should be marked inactive atomically with qc completion */
if (qc->tf.protocol == ATA_PROT_NCQ) {
link->sactive &= ~(1 << qc->tag);
if (!link->sactive)
ap->nr_active_links--;
} else {
link->active_tag = ATA_TAG_POISON;
ap->nr_active_links--;
}
/* clear exclusive status */
if (unlikely(qc->flags & ATA_QCFLAG_CLEAR_EXCL &&
ap->excl_link == link))
ap->excl_link = NULL;
/* atapi: mark qc as inactive to prevent the interrupt handler
* from completing the command twice later, before the error handler
* is called. (when rc != 0 and atapi request sense is needed)
*/
qc->flags &= ~ATA_QCFLAG_ACTIVE;
ap->qc_active &= ~(1 << qc->tag);
/* call completion callback */
qc->complete_fn(qc);
}
static void fill_result_tf(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
qc->result_tf.flags = qc->tf.flags;
ap->ops->qc_fill_rtf(qc);
}
static void ata_verify_xfer(struct ata_queued_cmd *qc)
{
struct ata_device *dev = qc->dev;
if (ata_is_nodata(qc->tf.protocol))
return;
if ((dev->mwdma_mask || dev->udma_mask) && ata_is_pio(qc->tf.protocol))
return;
dev->flags &= ~ATA_DFLAG_DUBIOUS_XFER;
}
/**
* ata_qc_complete - Complete an active ATA command
* @qc: Command to complete
*
* Indicate to the mid and upper layers that an ATA command has
* completed, with either an ok or not-ok status.
*
* Refrain from calling this function multiple times when
* successfully completing multiple NCQ commands.
* ata_qc_complete_multiple() should be used instead, which will
* properly update IRQ expect state.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
void ata_qc_complete(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
/* XXX: New EH and old EH use different mechanisms to
* synchronize EH with regular execution path.
*
* In new EH, a failed qc is marked with ATA_QCFLAG_FAILED.
* Normal execution path is responsible for not accessing a
* failed qc. libata core enforces the rule by returning NULL
* from ata_qc_from_tag() for failed qcs.
*
* Old EH depends on ata_qc_complete() nullifying completion
* requests if ATA_QCFLAG_EH_SCHEDULED is set. Old EH does
* not synchronize with interrupt handler. Only PIO task is
* taken care of.
*/
dev_info(&qc->dev->tdev, "%s: qc->dma_dir: %d", __func__, qc->dma_dir);
if (qc->dma_dir == DMA_TO_DEVICE) {
dev_info(&qc->dev->tdev, "%s: dma_sync_sg_for_device, qc->dma_dir: %d", __func__, qc->dma_dir);
dma_sync_sg_for_device(&qc->dev->tdev, qc->sg, qc->n_elem, qc->dma_dir);
} else if (qc->dma_dir == DMA_FROM_DEVICE) {
dev_info(&qc->dev->tdev, "%s: dma_sync_sg_for_cpu, qc->dma_dir: %d", __func__, qc->dma_dir);
dma_sync_sg_for_cpu(&qc->dev->tdev, qc->sg, qc->n_elem, qc->dma_dir);
}
if (ap->ops->error_handler) {
struct ata_device *dev = qc->dev;
struct ata_eh_info *ehi = &dev->link->eh_info;
if (unlikely(qc->err_mask))
qc->flags |= ATA_QCFLAG_FAILED;
/*
* Finish internal commands without any further processing
* and always with the result TF filled.
*/
if (unlikely(ata_tag_internal(qc->tag))) {
fill_result_tf(qc);
__ata_qc_complete(qc);
return;
}
/*
* Non-internal qc has failed. Fill the result TF and
* summon EH.
*/
if (unlikely(qc->flags & ATA_QCFLAG_FAILED)) {
fill_result_tf(qc);
ata_qc_schedule_eh(qc);
return;
}
WARN_ON_ONCE(ap->pflags & ATA_PFLAG_FROZEN);
/* read result TF if requested */
if (qc->flags & ATA_QCFLAG_RESULT_TF)
fill_result_tf(qc);
/* Some commands need post-processing after successful
* completion.
*/
switch (qc->tf.command) {
case ATA_CMD_SET_FEATURES:
if (qc->tf.feature != SETFEATURES_WC_ON &&
qc->tf.feature != SETFEATURES_WC_OFF)
break;
/* fall through */
case ATA_CMD_INIT_DEV_PARAMS: /* CHS translation changed */
case ATA_CMD_SET_MULTI: /* multi_count changed */
/* revalidate device */
ehi->dev_action[dev->devno] |= ATA_EH_REVALIDATE;
ata_port_schedule_eh(ap);
break;
case ATA_CMD_SLEEP:
dev->flags |= ATA_DFLAG_SLEEPING;
break;
}
if (unlikely(dev->flags & ATA_DFLAG_DUBIOUS_XFER))
ata_verify_xfer(qc);
__ata_qc_complete(qc);
} else {
if (qc->flags & ATA_QCFLAG_EH_SCHEDULED)
return;
/* read result TF if failed or requested */
if (qc->err_mask || qc->flags & ATA_QCFLAG_RESULT_TF)
fill_result_tf(qc);
__ata_qc_complete(qc);
}
}
/**
* ata_qc_complete_multiple - Complete multiple qcs successfully
* @ap: port in question
* @qc_active: new qc_active mask
*
* Complete in-flight commands. This functions is meant to be
* called from low-level driver's interrupt routine to complete
* requests normally. ap->qc_active and @qc_active is compared
* and commands are completed accordingly.
*
* Always use this function when completing multiple NCQ commands
* from IRQ handlers instead of calling ata_qc_complete()
* multiple times to keep IRQ expect status properly in sync.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*
* RETURNS:
* Number of completed commands on success, -errno otherwise.
*/
int ata_qc_complete_multiple(struct ata_port *ap, u32 qc_active)
{
int nr_done = 0;
u32 done_mask;
done_mask = ap->qc_active ^ qc_active;
if (unlikely(done_mask & qc_active)) {
ata_port_err(ap, "illegal qc_active transition (%08x->%08x)\n",
ap->qc_active, qc_active);
return -EINVAL;
}
while (done_mask) {
struct ata_queued_cmd *qc;
unsigned int tag = __ffs(done_mask);
qc = ata_qc_from_tag(ap, tag);
if (qc) {
ata_qc_complete(qc);
nr_done++;
}
done_mask &= ~(1 << tag);
}
return nr_done;
}
/**
* ata_qc_issue - issue taskfile to device
* @qc: command to issue to device
*
* Prepare an ATA command to submission to device.
* This includes mapping the data into a DMA-able
* area, filling in the S/G table, and finally
* writing the taskfile to hardware, starting the command.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
void ata_qc_issue(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct ata_link *link = qc->dev->link;
u8 prot = qc->tf.protocol;
/* Make sure only one non-NCQ command is outstanding. The
* check is skipped for old EH because it reuses active qc to
* request ATAPI sense.
*/
WARN_ON_ONCE(ap->ops->error_handler && ata_tag_valid(link->active_tag));
if (ata_is_ncq(prot)) {
WARN_ON_ONCE(link->sactive & (1 << qc->tag));
if (!link->sactive)
ap->nr_active_links++;
link->sactive |= 1 << qc->tag;
} else {
WARN_ON_ONCE(link->sactive);
ap->nr_active_links++;
link->active_tag = qc->tag;
}
qc->flags |= ATA_QCFLAG_ACTIVE;
ap->qc_active |= 1 << qc->tag;
/*
* We guarantee to LLDs that they will have at least one
* non-zero sg if the command is a data command.
*/
if (WARN_ON_ONCE(ata_is_data(prot) &&
(!qc->sg || !qc->n_elem || !qc->nbytes)))
goto sys_err;
if (ata_is_dma(prot) || (ata_is_pio(prot) &&
(ap->flags & ATA_FLAG_PIO_DMA)))
if (ata_sg_setup(qc))
goto sys_err;
/* if device is sleeping, schedule reset and abort the link */
if (unlikely(qc->dev->flags & ATA_DFLAG_SLEEPING)) {
link->eh_info.action |= ATA_EH_RESET;
ata_ehi_push_desc(&link->eh_info, "waking up from sleep");
ata_link_abort(link);
return;
}
ap->ops->qc_prep(qc);
qc->err_mask |= ap->ops->qc_issue(qc);
if (unlikely(qc->err_mask))
goto err;
return;
sys_err:
qc->err_mask |= AC_ERR_SYSTEM;
err:
ata_qc_complete(qc);
}
/**
* sata_scr_valid - test whether SCRs are accessible
* @link: ATA link to test SCR accessibility for
*
* Test whether SCRs are accessible for @link.
*
* LOCKING:
* None.
*
* RETURNS:
* 1 if SCRs are accessible, 0 otherwise.
*/
int sata_scr_valid(struct ata_link *link)
{
struct ata_port *ap = link->ap;
return (ap->flags & ATA_FLAG_SATA) && ap->ops->scr_read;
}
/**
* sata_scr_read - read SCR register of the specified port
* @link: ATA link to read SCR for
* @reg: SCR to read
* @val: Place to store read value
*
* Read SCR register @reg of @link into *@val. This function is
* guaranteed to succeed if @link is ap->link, the cable type of
* the port is SATA and the port implements ->scr_read.
*
* LOCKING:
* None if @link is ap->link. Kernel thread context otherwise.
*
* RETURNS:
* 0 on success, negative errno on failure.
*/
int sata_scr_read(struct ata_link *link, int reg, u32 *val)
{
if (ata_is_host_link(link)) {
if (sata_scr_valid(link))
return link->ap->ops->scr_read(link, reg, val);
return -EOPNOTSUPP;
}
return sata_pmp_scr_read(link, reg, val);
}
/**
* sata_scr_write - write SCR register of the specified port
* @link: ATA link to write SCR for
* @reg: SCR to write
* @val: value to write
*
* Write @val to SCR register @reg of @link. This function is
* guaranteed to succeed if @link is ap->link, the cable type of
* the port is SATA and the port implements ->scr_read.
*
* LOCKING:
* None if @link is ap->link. Kernel thread context otherwise.
*
* RETURNS:
* 0 on success, negative errno on failure.
*/
int sata_scr_write(struct ata_link *link, int reg, u32 val)
{
if (ata_is_host_link(link)) {
if (sata_scr_valid(link))
return link->ap->ops->scr_write(link, reg, val);
return -EOPNOTSUPP;
}
return sata_pmp_scr_write(link, reg, val);
}
/**
* sata_scr_write_flush - write SCR register of the specified port and flush
* @link: ATA link to write SCR for
* @reg: SCR to write
* @val: value to write
*
* This function is identical to sata_scr_write() except that this
* function performs flush after writing to the register.
*
* LOCKING:
* None if @link is ap->link. Kernel thread context otherwise.
*
* RETURNS:
* 0 on success, negative errno on failure.
*/
int sata_scr_write_flush(struct ata_link *link, int reg, u32 val)
{
if (ata_is_host_link(link)) {
int rc;
if (sata_scr_valid(link)) {
rc = link->ap->ops->scr_write(link, reg, val);
if (rc == 0)
rc = link->ap->ops->scr_read(link, reg, &val);
return rc;
}
return -EOPNOTSUPP;
}
return sata_pmp_scr_write(link, reg, val);
}
/**
* ata_phys_link_online - test whether the given link is online
* @link: ATA link to test
*
* Test whether @link is online. Note that this function returns
* 0 if online status of @link cannot be obtained, so
* ata_link_online(link) != !ata_link_offline(link).
*
* LOCKING:
* None.
*
* RETURNS:
* True if the port online status is available and online.
*/
bool ata_phys_link_online(struct ata_link *link)
{
u32 sstatus;
if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0 &&
ata_sstatus_online(sstatus))
return true;
return false;
}
/**
* ata_phys_link_offline - test whether the given link is offline
* @link: ATA link to test
*
* Test whether @link is offline. Note that this function
* returns 0 if offline status of @link cannot be obtained, so
* ata_link_online(link) != !ata_link_offline(link).
*
* LOCKING:
* None.
*
* RETURNS:
* True if the port offline status is available and offline.
*/
bool ata_phys_link_offline(struct ata_link *link)
{
u32 sstatus;
if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0 &&
!ata_sstatus_online(sstatus))
return true;
return false;
}
/**
* ata_link_online - test whether the given link is online
* @link: ATA link to test
*
* Test whether @link is online. This is identical to
* ata_phys_link_online() when there's no slave link. When
* there's a slave link, this function should only be called on
* the master link and will return true if any of M/S links is
* online.
*
* LOCKING:
* None.
*
* RETURNS:
* True if the port online status is available and online.
*/
bool ata_link_online(struct ata_link *link)
{
struct ata_link *slave = link->ap->slave_link;
WARN_ON(link == slave); /* shouldn't be called on slave link */
return ata_phys_link_online(link) ||
(slave && ata_phys_link_online(slave));
}
/**
* ata_link_offline - test whether the given link is offline
* @link: ATA link to test
*
* Test whether @link is offline. This is identical to
* ata_phys_link_offline() when there's no slave link. When
* there's a slave link, this function should only be called on
* the master link and will return true if both M/S links are
* offline.
*
* LOCKING:
* None.
*
* RETURNS:
* True if the port offline status is available and offline.
*/
bool ata_link_offline(struct ata_link *link)
{
struct ata_link *slave = link->ap->slave_link;
WARN_ON(link == slave); /* shouldn't be called on slave link */
return ata_phys_link_offline(link) &&
(!slave || ata_phys_link_offline(slave));
}
#ifdef CONFIG_PM
static void ata_port_request_pm(struct ata_port *ap, pm_message_t mesg,
unsigned int action, unsigned int ehi_flags,
bool async)
{
struct ata_link *link;
unsigned long flags;
/* Previous resume operation might still be in
* progress. Wait for PM_PENDING to clear.
*/
if (ap->pflags & ATA_PFLAG_PM_PENDING) {
ata_port_wait_eh(ap);
WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
}
/* request PM ops to EH */
spin_lock_irqsave(ap->lock, flags);
ap->pm_mesg = mesg;
ap->pflags |= ATA_PFLAG_PM_PENDING;
ata_for_each_link(link, ap, HOST_FIRST) {
link->eh_info.action |= action;
link->eh_info.flags |= ehi_flags;
}
ata_port_schedule_eh(ap);
spin_unlock_irqrestore(ap->lock, flags);
if (!async) {
ata_port_wait_eh(ap);
WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
}
}
/*
* On some hardware, device fails to respond after spun down for suspend. As
* the device won't be used before being resumed, we don't need to touch the
* device. Ask EH to skip the usual stuff and proceed directly to suspend.
*
* http://thread.gmane.org/gmane.linux.ide/46764
*/
static const unsigned int ata_port_suspend_ehi = ATA_EHI_QUIET
| ATA_EHI_NO_AUTOPSY
| ATA_EHI_NO_RECOVERY;
static void ata_port_suspend(struct ata_port *ap, pm_message_t mesg)
{
ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, false);
}
static void ata_port_suspend_async(struct ata_port *ap, pm_message_t mesg)
{
ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, true);
}
static int ata_port_pm_suspend(struct device *dev)
{
struct ata_port *ap = to_ata_port(dev);
if (pm_runtime_suspended(dev))
return 0;
ata_port_suspend(ap, PMSG_SUSPEND);
return 0;
}
static int ata_port_pm_freeze(struct device *dev)
{
struct ata_port *ap = to_ata_port(dev);
if (pm_runtime_suspended(dev))
return 0;
ata_port_suspend(ap, PMSG_FREEZE);
return 0;
}
static int ata_port_pm_poweroff(struct device *dev)
{
ata_port_suspend(to_ata_port(dev), PMSG_HIBERNATE);
return 0;
}
static const unsigned int ata_port_resume_ehi = ATA_EHI_NO_AUTOPSY
| ATA_EHI_QUIET;
static void ata_port_resume(struct ata_port *ap, pm_message_t mesg)
{
ata_port_request_pm(ap, mesg, ATA_EH_RESET, ata_port_resume_ehi, false);
}
static void ata_port_resume_async(struct ata_port *ap, pm_message_t mesg)
{
ata_port_request_pm(ap, mesg, ATA_EH_RESET, ata_port_resume_ehi, true);
}
static int ata_port_pm_resume(struct device *dev)
{
ata_port_resume_async(to_ata_port(dev), PMSG_RESUME);
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
}
/*
* For ODDs, the upper layer will poll for media change every few seconds,
* which will make it enter and leave suspend state every few seconds. And
* as each suspend will cause a hard/soft reset, the gain of runtime suspend
* is very little and the ODD may malfunction after constantly being reset.
* So the idle callback here will not proceed to suspend if a non-ZPODD capable
* ODD is attached to the port.
*/
static int ata_port_runtime_idle(struct device *dev)
{
struct ata_port *ap = to_ata_port(dev);
struct ata_link *link;
struct ata_device *adev;
ata_for_each_link(link, ap, HOST_FIRST) {
ata_for_each_dev(adev, link, ENABLED)
if (adev->class == ATA_DEV_ATAPI &&
!zpodd_dev_enabled(adev))
return -EBUSY;
}
return 0;
}
static int ata_port_runtime_suspend(struct device *dev)
{
ata_port_suspend(to_ata_port(dev), PMSG_AUTO_SUSPEND);
return 0;
}
static int ata_port_runtime_resume(struct device *dev)
{
ata_port_resume(to_ata_port(dev), PMSG_AUTO_RESUME);
return 0;
}
static const struct dev_pm_ops ata_port_pm_ops = {
.suspend = ata_port_pm_suspend,
.resume = ata_port_pm_resume,
.freeze = ata_port_pm_freeze,
.thaw = ata_port_pm_resume,
.poweroff = ata_port_pm_poweroff,
.restore = ata_port_pm_resume,
.runtime_suspend = ata_port_runtime_suspend,
.runtime_resume = ata_port_runtime_resume,
.runtime_idle = ata_port_runtime_idle,
};
/* sas ports don't participate in pm runtime management of ata_ports,
* and need to resume ata devices at the domain level, not the per-port
* level. sas suspend/resume is async to allow parallel port recovery
* since sas has multiple ata_port instances per Scsi_Host.
*/
void ata_sas_port_suspend(struct ata_port *ap)
{
ata_port_suspend_async(ap, PMSG_SUSPEND);
}
EXPORT_SYMBOL_GPL(ata_sas_port_suspend);
void ata_sas_port_resume(struct ata_port *ap)
{
ata_port_resume_async(ap, PMSG_RESUME);
}
EXPORT_SYMBOL_GPL(ata_sas_port_resume);
/**
* ata_host_suspend - suspend host
* @host: host to suspend
* @mesg: PM message
*
* Suspend @host. Actual operation is performed by port suspend.
*/
int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
{
host->dev->power.power_state = mesg;
return 0;
}
/**
* ata_host_resume - resume host
* @host: host to resume
*
* Resume @host. Actual operation is performed by port resume.
*/
void ata_host_resume(struct ata_host *host)
{
host->dev->power.power_state = PMSG_ON;
}
#endif
struct device_type ata_port_type = {
.name = "ata_port",
#ifdef CONFIG_PM
.pm = &ata_port_pm_ops,
#endif
};
/**
* ata_dev_init - Initialize an ata_device structure
* @dev: Device structure to initialize
*
* Initialize @dev in preparation for probing.
*
* LOCKING:
* Inherited from caller.
*/
void ata_dev_init(struct ata_device *dev)
{
struct ata_link *link = ata_dev_phys_link(dev);
struct ata_port *ap = link->ap;
unsigned long flags;
/* SATA spd limit is bound to the attached device, reset together */
link->sata_spd_limit = link->hw_sata_spd_limit;
link->sata_spd = 0;
/* High bits of dev->flags are used to record warm plug
* requests which occur asynchronously. Synchronize using
* host lock.
*/
spin_lock_irqsave(ap->lock, flags);
dev->flags &= ~ATA_DFLAG_INIT_MASK;
dev->horkage = 0;
spin_unlock_irqrestore(ap->lock, flags);
memset((void *)dev + ATA_DEVICE_CLEAR_BEGIN, 0,
ATA_DEVICE_CLEAR_END - ATA_DEVICE_CLEAR_BEGIN);
dev->pio_mask = UINT_MAX;
dev->mwdma_mask = UINT_MAX;
dev->udma_mask = UINT_MAX;
}
/**
* ata_link_init - Initialize an ata_link structure
* @ap: ATA port link is attached to
* @link: Link structure to initialize
* @pmp: Port multiplier port number
*
* Initialize @link.
*
* LOCKING:
* Kernel thread context (may sleep)
*/
void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp)
{
int i;
/* clear everything except for devices */
memset((void *)link + ATA_LINK_CLEAR_BEGIN, 0,
ATA_LINK_CLEAR_END - ATA_LINK_CLEAR_BEGIN);
link->ap = ap;
link->pmp = pmp;
link->active_tag = ATA_TAG_POISON;
link->hw_sata_spd_limit = UINT_MAX;
/* can't use iterator, ap isn't initialized yet */
for (i = 0; i < ATA_MAX_DEVICES; i++) {
struct ata_device *dev = &link->device[i];
dev->link = link;
dev->devno = dev - link->device;
#ifdef CONFIG_ATA_ACPI
dev->gtf_filter = ata_acpi_gtf_filter;
#endif
ata_dev_init(dev);
}
}
/**
* sata_link_init_spd - Initialize link->sata_spd_limit
* @link: Link to configure sata_spd_limit for
*
* Initialize @link->[hw_]sata_spd_limit to the currently
* configured value.
*
* LOCKING:
* Kernel thread context (may sleep).
*
* RETURNS:
* 0 on success, -errno on failure.
*/
int sata_link_init_spd(struct ata_link *link)
{
u8 spd;
int rc;
rc = sata_scr_read(link, SCR_CONTROL, &link->saved_scontrol);
if (rc)
return rc;
spd = (link->saved_scontrol >> 4) & 0xf;
if (spd)
link->hw_sata_spd_limit &= (1 << spd) - 1;
ata_force_link_limits(link);
link->sata_spd_limit = link->hw_sata_spd_limit;
return 0;
}
/**
* ata_port_alloc - allocate and initialize basic ATA port resources
* @host: ATA host this allocated port belongs to
*
* Allocate and initialize basic ATA port resources.
*
* RETURNS:
* Allocate ATA port on success, NULL on failure.
*
* LOCKING:
* Inherited from calling layer (may sleep).
*/
struct ata_port *ata_port_alloc(struct ata_host *host)
{
struct ata_port *ap;
DPRINTK("ENTER\n");
ap = kzalloc(sizeof(*ap), GFP_KERNEL);
if (!ap)
return NULL;
ap->pflags |= ATA_PFLAG_INITIALIZING | ATA_PFLAG_FROZEN;
ap->lock = &host->lock;
ap->print_id = -1;
ap->local_port_no = -1;
ap->host = host;
ap->dev = host->dev;
#if defined(ATA_VERBOSE_DEBUG)
/* turn on all debugging levels */
ap->msg_enable = 0x00FF;
#elif defined(ATA_DEBUG)
ap->msg_enable = ATA_MSG_DRV | ATA_MSG_INFO | ATA_MSG_CTL | ATA_MSG_WARN | ATA_MSG_ERR;
#else
ap->msg_enable = ATA_MSG_DRV | ATA_MSG_ERR | ATA_MSG_WARN;
#endif
mutex_init(&ap->scsi_scan_mutex);
INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug);
INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
INIT_LIST_HEAD(&ap->eh_done_q);
init_waitqueue_head(&ap->eh_wait_q);
init_completion(&ap->park_req_pending);
init_timer_deferrable(&ap->fastdrain_timer);
ap->fastdrain_timer.function = ata_eh_fastdrain_timerfn;
ap->fastdrain_timer.data = (unsigned long)ap;
ap->cbl = ATA_CBL_NONE;
ata_link_init(ap, &ap->link, 0);
#ifdef ATA_IRQ_TRAP
ap->stats.unhandled_irq = 1;
ap->stats.idle_irq = 1;
#endif
ata_sff_port_init(ap);
return ap;
}
static void ata_host_release(struct device *gendev, void *res)
{
struct ata_host *host = dev_get_drvdata(gendev);
int i;
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
if (!ap)
continue;
if (ap->scsi_host)
scsi_host_put(ap->scsi_host);
kfree(ap->pmp_link);
kfree(ap->slave_link);
kfree(ap);
host->ports[i] = NULL;
}
dev_set_drvdata(gendev, NULL);
}
/**
* ata_host_alloc - allocate and init basic ATA host resources
* @dev: generic device this host is associated with
* @max_ports: maximum number of ATA ports associated with this host
*
* Allocate and initialize basic ATA host resources. LLD calls
* this function to allocate a host, initializes it fully and
* attaches it using ata_host_register().
*
* @max_ports ports are allocated and host->n_ports is
* initialized to @max_ports. The caller is allowed to decrease
* host->n_ports before calling ata_host_register(). The unused
* ports will be automatically freed on registration.
*
* RETURNS:
* Allocate ATA host on success, NULL on failure.
*
* LOCKING:
* Inherited from calling layer (may sleep).
*/
struct ata_host *ata_host_alloc(struct device *dev, int max_ports)
{
struct ata_host *host;
size_t sz;
int i;
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);
if (!host)
goto err_out;
devres_add(dev, host);
dev_set_drvdata(dev, host);
spin_lock_init(&host->lock);
mutex_init(&host->eh_mutex);
host->dev = dev;
host->n_ports = max_ports;
/* allocate ports bound to this host */
for (i = 0; i < max_ports; i++) {
struct ata_port *ap;
ap = ata_port_alloc(host);
if (!ap)
goto err_out;
ap->port_no = i;
host->ports[i] = ap;
}
devres_remove_group(dev, NULL);
return host;
err_out:
devres_release_group(dev, NULL);
return NULL;
}
/**
* ata_host_alloc_pinfo - alloc host and init with port_info array
* @dev: generic device this host is associated with
* @ppi: array of ATA port_info to initialize host with
* @n_ports: number of ATA ports attached to this host
*
* Allocate ATA host and initialize with info from @ppi. If NULL
* terminated, @ppi may contain fewer entries than @n_ports. The
* last entry will be used for the remaining ports.
*
* RETURNS:
* Allocate ATA host on success, NULL on failure.
*
* LOCKING:
* Inherited from calling layer (may sleep).
*/
struct ata_host *ata_host_alloc_pinfo(struct device *dev,
const struct ata_port_info * const * ppi,
int n_ports)
{
const struct ata_port_info *pi;
struct ata_host *host;
int i, j;
host = ata_host_alloc(dev, n_ports);
if (!host)
return NULL;
for (i = 0, j = 0, pi = NULL; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
if (ppi[j])
pi = ppi[j++];
ap->pio_mask = pi->pio_mask;
ap->mwdma_mask = pi->mwdma_mask;
ap->udma_mask = pi->udma_mask;
ap->flags |= pi->flags;
ap->link.flags |= pi->link_flags;
ap->ops = pi->port_ops;
if (!host->ops && (pi->port_ops != &ata_dummy_port_ops))
host->ops = pi->port_ops;
}
return host;
}
/**
* ata_slave_link_init - initialize slave link
* @ap: port to initialize slave link for
*
* Create and initialize slave link for @ap. This enables slave
* link handling on the port.
*
* In libata, a port contains links and a link contains devices.
* There is single host link but if a PMP is attached to it,
* there can be multiple fan-out links. On SATA, there's usually
* a single device connected to a link but PATA and SATA
* controllers emulating TF based interface can have two - master
* and slave.
*
* However, there are a few controllers which don't fit into this
* abstraction too well - SATA controllers which emulate TF
* interface with both master and slave devices but also have
* separate SCR register sets for each device. These controllers
* need separate links for physical link handling
* (e.g. onlineness, link speed) but should be treated like a
* traditional M/S controller for everything else (e.g. command
* issue, softreset).
*
* slave_link is libata's way of handling this class of
* controllers without impacting core layer too much. For
* anything other than physical link handling, the default host
* link is used for both master and slave. For physical link
* handling, separate @ap->slave_link is used. All dirty details
* are implemented inside libata core layer. From LLD's POV, the
* only difference is that prereset, hardreset and postreset are
* called once more for the slave link, so the reset sequence
* looks like the following.
*
* prereset(M) -> prereset(S) -> hardreset(M) -> hardreset(S) ->
* softreset(M) -> postreset(M) -> postreset(S)
*
* Note that softreset is called only for the master. Softreset
* resets both M/S by definition, so SRST on master should handle
* both (the standard method will work just fine).
*
* LOCKING:
* Should be called before host is registered.
*
* RETURNS:
* 0 on success, -errno on failure.
*/
int ata_slave_link_init(struct ata_port *ap)
{
struct ata_link *link;
WARN_ON(ap->slave_link);
WARN_ON(ap->flags & ATA_FLAG_PMP);
link = kzalloc(sizeof(*link), GFP_KERNEL);
if (!link)
return -ENOMEM;
ata_link_init(ap, link, 1);
ap->slave_link = link;
return 0;
}
static void ata_host_stop(struct device *gendev, void *res)
{
struct ata_host *host = dev_get_drvdata(gendev);
int i;
WARN_ON(!(host->flags & ATA_HOST_STARTED));
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
if (ap->ops->port_stop)
ap->ops->port_stop(ap);
}
if (host->ops->host_stop)
host->ops->host_stop(host);
}
/**
* ata_finalize_port_ops - finalize ata_port_operations
* @ops: ata_port_operations to finalize
*
* An ata_port_operations can inherit from another ops and that
* ops can again inherit from another. This can go on as many
* times as necessary as long as there is no loop in the
* inheritance chain.
*
* Ops tables are finalized when the host is started. NULL or
* unspecified entries are inherited from the closet ancestor
* which has the method and the entry is populated with it.
* After finalization, the ops table directly points to all the
* methods and ->inherits is no longer necessary and cleared.
*
* Using ATA_OP_NULL, inheriting ops can force a method to NULL.
*
* LOCKING:
* None.
*/
static void ata_finalize_port_ops(struct ata_port_operations *ops)
{
static DEFINE_SPINLOCK(lock);
const struct ata_port_operations *cur;
void **begin = (void **)ops;
void **end = (void **)&ops->inherits;
void **pp;
if (!ops || !ops->inherits)
return;
spin_lock(&lock);
for (cur = ops->inherits; cur; cur = cur->inherits) {
void **inherit = (void **)cur;
for (pp = begin; pp < end; pp++, inherit++)
if (!*pp)
*pp = *inherit;
}
for (pp = begin; pp < end; pp++)
if (IS_ERR(*pp))
*pp = NULL;
ops->inherits = NULL;
spin_unlock(&lock);
}
/**
* ata_host_start - start and freeze ports of an ATA host
* @host: ATA host to start ports for
*
* Start and then freeze ports of @host. Started status is
* recorded in host->flags, so this function can be called
* multiple times. Ports are guaranteed to get started only
* once. If host->ops isn't initialized yet, its set to the
* first non-dummy port ops.
*
* LOCKING:
* Inherited from calling layer (may sleep).
*
* RETURNS:
* 0 if all ports are started successfully, -errno otherwise.
*/
int ata_host_start(struct ata_host *host)
{
int have_stop = 0;
void *start_dr = NULL;
int i, rc;
dev_info(host->ports[0]->host->dev, "libata-core.c ata_host_start(), 0x%08x\n",host->flags);
if (host->flags & ATA_HOST_STARTED)
return 0;
ata_finalize_port_ops(host->ops);
dev_info(host->ports[0]->host->dev, "libata-core.c ata_host_start(): ata_finalize_port_ops() DONE");
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
ata_finalize_port_ops(ap->ops);
if (!host->ops && !ata_port_is_dummy(ap))
host->ops = ap->ops;
if (ap->ops->port_stop)
have_stop = 1;
}
if (host->ops->host_stop)
have_stop = 1;
if (have_stop) {
start_dr = devres_alloc(ata_host_stop, 0, GFP_KERNEL);
if (!start_dr)
return -ENOMEM;
}
dev_info(host->ports[0]->host->dev, "libata-core.c ata_host_start(): Checked for 'have stop' = %d",have_stop);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
if (ap->ops->port_start) {
rc = ap->ops->port_start(ap);
if (rc) {
if (rc != -ENODEV)
dev_err(host->dev,
"failed to start port %d (errno=%d)\n",
i, rc);
goto err_out;
}
}
ata_eh_freeze_port(ap);
dev_info(host->ports[0]->host->dev, "ata_host_start(): freezing port %d",i);
}
if (start_dr)
devres_add(host->dev, start_dr);
host->flags |= ATA_HOST_STARTED;
dev_info(host->ports[0]->host->dev, "ata_host_start() OK, host->flags = 0x%08x",host->flags);
return 0;
err_out:
while (--i >= 0) {
struct ata_port *ap = host->ports[i];
if (ap->ops->port_stop)
ap->ops->port_stop(ap);
}
dev_info(host->ports[0]->host->dev, "ata_host_start() err_out, host->flags = 0x%08x (called port_stop)",host->flags);
devres_free(start_dr);
return rc;
}
/**
* ata_sas_host_init - Initialize a host struct for sas (ipr, libsas)
* @host: host to initialize
* @dev: device host is attached to
* @ops: port_ops
*
*/
void ata_host_init(struct ata_host *host, struct device *dev,
struct ata_port_operations *ops)
{
spin_lock_init(&host->lock);
mutex_init(&host->eh_mutex);
host->n_tags = ATA_MAX_QUEUE - 1;
host->dev = dev;
host->ops = ops;
}
void __ata_port_probe(struct ata_port *ap)
{
struct ata_eh_info *ehi = &ap->link.eh_info;
unsigned long flags;
/* kick EH for boot probing */
spin_lock_irqsave(ap->lock, flags);
ehi->probe_mask |= ATA_ALL_DEVICES;
ehi->action |= ATA_EH_RESET;
ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;
ap->pflags &= ~ATA_PFLAG_INITIALIZING;
ap->pflags |= ATA_PFLAG_LOADING;
ata_port_schedule_eh(ap);
spin_unlock_irqrestore(ap->lock, flags);
}
int ata_port_probe(struct ata_port *ap)
{
int rc = 0;
if (ap->ops->error_handler) {
__ata_port_probe(ap);
ata_port_wait_eh(ap);
} else {
DPRINTK("ata%u: bus probe begin\n", ap->print_id);
rc = ata_bus_probe(ap);
DPRINTK("ata%u: bus probe end\n", ap->print_id);
}
return rc;
}
static void async_port_probe(void *data, async_cookie_t cookie)
{
struct ata_port *ap = data;
/*
* If we're not allowed to scan this host in parallel,
* we need to wait until all previous scans have completed
* before going further.
* Jeff Garzik says this is only within a controller, so we
* don't need to wait for port 0, only for later ports.
*/
if (!(ap->host->flags & ATA_HOST_PARALLEL_SCAN) && ap->port_no != 0)
async_synchronize_cookie(cookie);
(void)ata_port_probe(ap);
/* in order to keep device order, we need to synchronize at this point */
async_synchronize_cookie(cookie);
ata_scsi_scan_host(ap, 1);
}
/**
* ata_host_register - register initialized ATA host
* @host: ATA host to register
* @sht: template for SCSI host
*
* Register initialized ATA host. @host is allocated using
* ata_host_alloc() and fully initialized by LLD. This function
* starts ports, registers @host with ATA and SCSI layers and
* probe registered devices.
*
* LOCKING:
* Inherited from calling layer (may sleep).
*
* RETURNS:
* 0 on success, -errno otherwise.
*/
int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
{
int i, rc;
host->n_tags = clamp(sht->can_queue, 1, ATA_MAX_QUEUE - 1);
/* host must have been started */
if (!(host->flags & ATA_HOST_STARTED)) {
dev_err(host->dev, "BUG: trying to register unstarted host\n");
WARN_ON(1);
return -EINVAL;
}
/* Blow away unused ports. This happens when LLD can't
* determine the exact number of ports to allocate at
* allocation time.
*/
for (i = host->n_ports; host->ports[i]; i++)
kfree(host->ports[i]);
/* give ports names and add SCSI hosts */
for (i = 0; i < host->n_ports; i++) {
host->ports[i]->print_id = atomic_inc_return(&ata_print_id);
host->ports[i]->local_port_no = i + 1;
}
/* Create associated sysfs transport objects */
for (i = 0; i < host->n_ports; i++) {
rc = ata_tport_add(host->dev,host->ports[i]);
if (rc) {
goto err_tadd;
}
}
rc = ata_scsi_add_hosts(host, sht);
if (rc)
goto err_tadd;
/* set cable, sata_spd_limit and report */
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
unsigned long xfer_mask;
/* set SATA cable type if still unset */
if (ap->cbl == ATA_CBL_NONE && (ap->flags & ATA_FLAG_SATA))
ap->cbl = ATA_CBL_SATA;
/* init sata_spd_limit to the current value */
sata_link_init_spd(&ap->link);
if (ap->slave_link)
sata_link_init_spd(ap->slave_link);
/* print per-port info to dmesg */
xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask,
ap->udma_mask);
if (!ata_port_is_dummy(ap)) {
ata_port_info(ap, "%cATA max %s %s\n",
(ap->flags & ATA_FLAG_SATA) ? 'S' : 'P',
ata_mode_string(xfer_mask),
ap->link.eh_info.desc);
ata_ehi_clear_desc(&ap->link.eh_info);
} else
ata_port_info(ap, "DUMMY\n");
}
/* perform each probe asynchronously */
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
async_schedule(async_port_probe, ap);
}
return 0;
err_tadd:
while (--i >= 0) {
ata_tport_delete(host->ports[i]);
}
return rc;
}
/**
* ata_host_activate - start host, request IRQ and register it
* @host: target ATA host
* @irq: IRQ to request
* @irq_handler: irq_handler used when requesting IRQ
* @irq_flags: irq_flags used when requesting IRQ
* @sht: scsi_host_template to use when registering the host
*
* After allocating an ATA host and initializing it, most libata
* LLDs perform three steps to activate the host - start host,
* request IRQ and register it. This helper takes necessasry
* arguments and performs the three steps in one go.
*
* An invalid IRQ skips the IRQ registration and expects the host to
* have set polling mode on the port. In this case, @irq_handler
* should be NULL.
*
* LOCKING:
* Inherited from calling layer (may sleep).
*
* RETURNS:
* 0 on success, -errno otherwise.
*/
int ata_host_activate(struct ata_host *host, int irq,
irq_handler_t irq_handler, unsigned long irq_flags,
struct scsi_host_template *sht)
{
int i, rc;
rc = ata_host_start(host);
if (rc)
return rc;
/* Special case for polling mode */
if (!irq) {
WARN_ON(irq_handler);
return ata_host_register(host, sht);
}
rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags,
dev_name(host->dev), host);
if (rc)
return rc;
for (i = 0; i < host->n_ports; i++)
ata_port_desc(host->ports[i], "irq %d", irq);
rc = ata_host_register(host, sht);
/* if failed, just free the IRQ and leave ports alone */
if (rc)
devm_free_irq(host->dev, irq, host);
return rc;
}
/**
* ata_port_detach - Detach ATA port in prepration of device removal
* @ap: ATA port to be detached
*
* Detach all ATA devices and the associated SCSI devices of @ap;
* then, remove the associated SCSI host. @ap is guaranteed to
* be quiescent on return from this function.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
static void ata_port_detach(struct ata_port *ap)
{
unsigned long flags;
struct ata_link *link;
struct ata_device *dev;
if (!ap->ops->error_handler)
goto skip_eh;
/* tell EH we're leaving & flush EH */
spin_lock_irqsave(ap->lock, flags);
ap->pflags |= ATA_PFLAG_UNLOADING;
ata_port_schedule_eh(ap);
spin_unlock_irqrestore(ap->lock, flags);
/* wait till EH commits suicide */
ata_port_wait_eh(ap);
/* it better be dead now */
WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED));
cancel_delayed_work_sync(&ap->hotplug_task);
skip_eh:
/* clean up zpodd on port removal */
ata_for_each_link(link, ap, HOST_FIRST) {
ata_for_each_dev(dev, link, ALL) {
if (zpodd_dev_enabled(dev))
zpodd_exit(dev);
}
}
if (ap->pmp_link) {
int i;
for (i = 0; i < SATA_PMP_MAX_PORTS; i++)
ata_tlink_delete(&ap->pmp_link[i]);
}
/* remove the associated SCSI host */
scsi_remove_host(ap->scsi_host);
ata_tport_delete(ap);
}
/**
* ata_host_detach - Detach all ports of an ATA host
* @host: Host to detach
*
* Detach all ports of @host.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
void ata_host_detach(struct ata_host *host)
{
int i;
for (i = 0; i < host->n_ports; i++)
ata_port_detach(host->ports[i]);
/* the host is dead now, dissociate ACPI */
ata_acpi_dissociate(host);
}
#ifdef CONFIG_PCI
/**
* ata_pci_remove_one - PCI layer callback for device removal
* @pdev: PCI device that was removed
*
* PCI layer indicates to libata via this hook that hot-unplug or
* module unload event has occurred. Detach all ports. Resource
* release is handled via devres.
*
* LOCKING:
* Inherited from PCI layer (may sleep).
*/
void ata_pci_remove_one(struct pci_dev *pdev)
{
struct ata_host *host = pci_get_drvdata(pdev);
ata_host_detach(host);
}
/* move to PCI subsystem */
int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits)
{
unsigned long tmp = 0;
switch (bits->width) {
case 1: {
u8 tmp8 = 0;
pci_read_config_byte(pdev, bits->reg, &tmp8);
tmp = tmp8;
break;
}
case 2: {
u16 tmp16 = 0;
pci_read_config_word(pdev, bits->reg, &tmp16);
tmp = tmp16;
break;
}
case 4: {
u32 tmp32 = 0;
pci_read_config_dword(pdev, bits->reg, &tmp32);
tmp = tmp32;
break;
}
default:
return -EINVAL;
}
tmp &= bits->mask;
return (tmp == bits->val) ? 1 : 0;
}
#ifdef CONFIG_PM
void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
pci_save_state(pdev);
pci_disable_device(pdev);
if (mesg.event & PM_EVENT_SLEEP)
pci_set_power_state(pdev, PCI_D3hot);
}
int ata_pci_device_do_resume(struct pci_dev *pdev)
{
int rc;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
rc = pcim_enable_device(pdev);
if (rc) {
dev_err(&pdev->dev,
"failed to enable device after resume (%d)\n", rc);
return rc;
}
pci_set_master(pdev);
return 0;
}
int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
struct ata_host *host = pci_get_drvdata(pdev);
int rc = 0;
rc = ata_host_suspend(host, mesg);
if (rc)
return rc;
ata_pci_device_do_suspend(pdev, mesg);
return 0;
}
int ata_pci_device_resume(struct pci_dev *pdev)
{
struct ata_host *host = pci_get_drvdata(pdev);
int rc;
rc = ata_pci_device_do_resume(pdev);
if (rc == 0)
ata_host_resume(host);
return rc;
}
#endif /* CONFIG_PM */
#endif /* CONFIG_PCI */
/**
* ata_platform_remove_one - Platform layer callback for device removal
* @pdev: Platform device that was removed
*
* Platform layer indicates to libata via this hook that hot-unplug or
* module unload event has occurred. Detach all ports. Resource
* release is handled via devres.
*
* LOCKING:
* Inherited from platform layer (may sleep).
*/
int ata_platform_remove_one(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
ata_host_detach(host);
return 0;
}
static int __init ata_parse_force_one(char **cur,
struct ata_force_ent *force_ent,
const char **reason)
{
/* FIXME: Currently, there's no way to tag init const data and
* using __initdata causes build failure on some versions of
* gcc. Once __initdataconst is implemented, add const to the
* following structure.
*/
static struct ata_force_param force_tbl[] __initdata = {
{ "40c", .cbl = ATA_CBL_PATA40 },
{ "80c", .cbl = ATA_CBL_PATA80 },
{ "short40c", .cbl = ATA_CBL_PATA40_SHORT },
{ "unk", .cbl = ATA_CBL_PATA_UNK },
{ "ign", .cbl = ATA_CBL_PATA_IGN },
{ "sata", .cbl = ATA_CBL_SATA },
{ "1.5Gbps", .spd_limit = 1 },
{ "3.0Gbps", .spd_limit = 2 },
{ "noncq", .horkage_on = ATA_HORKAGE_NONCQ },
{ "ncq", .horkage_off = ATA_HORKAGE_NONCQ },
{ "dump_id", .horkage_on = ATA_HORKAGE_DUMP_ID },
{ "pio0", .xfer_mask = 1 << (ATA_SHIFT_PIO + 0) },
{ "pio1", .xfer_mask = 1 << (ATA_SHIFT_PIO + 1) },
{ "pio2", .xfer_mask = 1 << (ATA_SHIFT_PIO + 2) },
{ "pio3", .xfer_mask = 1 << (ATA_SHIFT_PIO + 3) },
{ "pio4", .xfer_mask = 1 << (ATA_SHIFT_PIO + 4) },
{ "pio5", .xfer_mask = 1 << (ATA_SHIFT_PIO + 5) },
{ "pio6", .xfer_mask = 1 << (ATA_SHIFT_PIO + 6) },
{ "mwdma0", .xfer_mask = 1 << (ATA_SHIFT_MWDMA + 0) },
{ "mwdma1", .xfer_mask = 1 << (ATA_SHIFT_MWDMA + 1) },
{ "mwdma2", .xfer_mask = 1 << (ATA_SHIFT_MWDMA + 2) },
{ "mwdma3", .xfer_mask = 1 << (ATA_SHIFT_MWDMA + 3) },
{ "mwdma4", .xfer_mask = 1 << (ATA_SHIFT_MWDMA + 4) },
{ "udma0", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 0) },
{ "udma16", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 0) },
{ "udma/16", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 0) },
{ "udma1", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 1) },
{ "udma25", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 1) },
{ "udma/25", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 1) },
{ "udma2", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 2) },
{ "udma33", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 2) },
{ "udma/33", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 2) },
{ "udma3", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 3) },
{ "udma44", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 3) },
{ "udma/44", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 3) },
{ "udma4", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 4) },
{ "udma66", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 4) },
{ "udma/66", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 4) },
{ "udma5", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 5) },
{ "udma100", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 5) },
{ "udma/100", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 5) },
{ "udma6", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 6) },
{ "udma133", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 6) },
{ "udma/133", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 6) },
{ "udma7", .xfer_mask = 1 << (ATA_SHIFT_UDMA + 7) },
{ "nohrst", .lflags = ATA_LFLAG_NO_HRST },
{ "nosrst", .lflags = ATA_LFLAG_NO_SRST },
{ "norst", .lflags = ATA_LFLAG_NO_HRST | ATA_LFLAG_NO_SRST },
{ "rstonce", .lflags = ATA_LFLAG_RST_ONCE },
{ "atapi_dmadir", .horkage_on = ATA_HORKAGE_ATAPI_DMADIR },
{ "disable", .horkage_on = ATA_HORKAGE_DISABLE },
};
char *start = *cur, *p = *cur;
char *id, *val, *endp;
const struct ata_force_param *match_fp = NULL;
int nr_matches = 0, i;
/* find where this param ends and update *cur */
while (*p != '\0' && *p != ',')
p++;
if (*p == '\0')
*cur = p;
else
*cur = p + 1;
*p = '\0';
/* parse */
p = strchr(start, ':');
if (!p) {
val = strstrip(start);
goto parse_val;
}
*p = '\0';
id = strstrip(start);
val = strstrip(p + 1);
/* parse id */
p = strchr(id, '.');
if (p) {
*p++ = '\0';
force_ent->device = simple_strtoul(p, &endp, 10);
if (p == endp || *endp != '\0') {
*reason = "invalid device";
return -EINVAL;
}
}
force_ent->port = simple_strtoul(id, &endp, 10);
if (p == endp || *endp != '\0') {
*reason = "invalid port/link";
return -EINVAL;
}
parse_val:
/* parse val, allow shortcuts so that both 1.5 and 1.5Gbps work */
for (i = 0; i < ARRAY_SIZE(force_tbl); i++) {
const struct ata_force_param *fp = &force_tbl[i];
if (strncasecmp(val, fp->name, strlen(val)))
continue;
nr_matches++;
match_fp = fp;
if (strcasecmp(val, fp->name) == 0) {
nr_matches = 1;
break;
}
}
if (!nr_matches) {
*reason = "unknown value";
return -EINVAL;
}
if (nr_matches > 1) {
*reason = "ambigious value";
return -EINVAL;
}
force_ent->param = *match_fp;
return 0;
}
static void __init ata_parse_force_param(void)
{
int idx = 0, size = 1;
int last_port = -1, last_device = -1;
char *p, *cur, *next;
/* calculate maximum number of params and allocate force_tbl */
for (p = ata_force_param_buf; *p; p++)
if (*p == ',')
size++;
ata_force_tbl = kzalloc(sizeof(ata_force_tbl[0]) * size, GFP_KERNEL);
if (!ata_force_tbl) {
printk(KERN_WARNING "ata: failed to extend force table, "
"libata.force ignored\n");
return;
}
/* parse and populate the table */
for (cur = ata_force_param_buf; *cur != '\0'; cur = next) {
const char *reason = "";
struct ata_force_ent te = { .port = -1, .device = -1 };
next = cur;
if (ata_parse_force_one(&next, &te, &reason)) {
printk(KERN_WARNING "ata: failed to parse force "
"parameter \"%s\" (%s)\n",
cur, reason);
continue;
}
if (te.port == -1) {
te.port = last_port;
te.device = last_device;
}
ata_force_tbl[idx++] = te;
last_port = te.port;
last_device = te.device;
}
ata_force_tbl_size = idx;
}
static int __init ata_init(void)
{
int rc;
ata_parse_force_param();
rc = ata_sff_init();
if (rc) {
kfree(ata_force_tbl);
return rc;
}
libata_transport_init();
ata_scsi_transport_template = ata_attach_transport();
if (!ata_scsi_transport_template) {
ata_sff_exit();
rc = -ENOMEM;
goto err_out;
}
printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n");
return 0;
err_out:
return rc;
}
static void __exit ata_exit(void)
{
ata_release_transport(ata_scsi_transport_template);
libata_transport_exit();
ata_sff_exit();
kfree(ata_force_tbl);
}
subsys_initcall(ata_init);
module_exit(ata_exit);
static DEFINE_RATELIMIT_STATE(ratelimit, HZ / 5, 1);
int ata_ratelimit(void)
{
return __ratelimit(&ratelimit);
}
/**
* ata_msleep - ATA EH owner aware msleep
* @ap: ATA port to attribute the sleep to
* @msecs: duration to sleep in milliseconds
*
* Sleeps @msecs. If the current task is owner of @ap's EH, the
* ownership is released before going to sleep and reacquired
* after the sleep is complete. IOW, other ports sharing the
* @ap->host will be allowed to own the EH while this task is
* sleeping.
*
* LOCKING:
* Might sleep.
*/
void ata_msleep(struct ata_port *ap, unsigned int msecs)
{
bool owns_eh = ap && ap->host->eh_owner == current;
if (owns_eh)
ata_eh_release(ap);
msleep(msecs);
if (owns_eh)
ata_eh_acquire(ap);
}
/**
* ata_wait_register - wait until register value changes
* @ap: ATA port to wait register for, can be NULL
* @reg: IO-mapped register
* @mask: Mask to apply to read register value
* @val: Wait condition
* @interval: polling interval in milliseconds
* @timeout: timeout in milliseconds
*
* Waiting for some bits of register to change is a common
* operation for ATA controllers. This function reads 32bit LE
* IO-mapped register @reg and tests for the following condition.
*
* (*@reg & mask) != val
*
* If the condition is met, it returns; otherwise, the process is
* repeated after @interval_msec until timeout.
*
* LOCKING:
* Kernel thread context (may sleep)
*
* RETURNS:
* The final register value.
*/
u32 ata_wait_register(struct ata_port *ap, void __iomem *reg, u32 mask, u32 val,
unsigned long interval, unsigned long timeout)
{
unsigned long deadline;
u32 tmp;
tmp = ioread32(reg);
/* Calculate timeout _after_ the first read to make sure
* preceding writes reach the controller before starting to
* eat away the timeout.
*/
deadline = ata_deadline(jiffies, timeout);
while ((tmp & mask) == val && time_before(jiffies, deadline)) {
ata_msleep(ap, interval);
tmp = ioread32(reg);
}
return tmp;
}
/*
* Dummy port_ops
*/
static unsigned int ata_dummy_qc_issue(struct ata_queued_cmd *qc)
{
return AC_ERR_SYSTEM;
}
static void ata_dummy_error_handler(struct ata_port *ap)
{
/* truly dummy */
}
struct ata_port_operations ata_dummy_port_ops = {
.qc_prep = ata_noop_qc_prep,
.qc_issue = ata_dummy_qc_issue,
.error_handler = ata_dummy_error_handler,
.sched_eh = ata_std_sched_eh,
.end_eh = ata_std_end_eh,
};
const struct ata_port_info ata_dummy_port_info = {
.port_ops = &ata_dummy_port_ops,
};
/*
* Utility print functions
*/
void ata_port_printk(const struct ata_port *ap, const char *level,
const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
printk("%sata%u: %pV", level, ap->print_id, &vaf);
va_end(args);
}
EXPORT_SYMBOL(ata_port_printk);
void ata_link_printk(const struct ata_link *link, const char *level,
const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
if (sata_pmp_attached(link->ap) || link->ap->slave_link)
printk("%sata%u.%02u: %pV",
level, link->ap->print_id, link->pmp, &vaf);
else
printk("%sata%u: %pV",
level, link->ap->print_id, &vaf);
va_end(args);
}
EXPORT_SYMBOL(ata_link_printk);
void ata_dev_printk(const struct ata_device *dev, const char *level,
const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
printk("%sata%u.%02u: %pV",
level, dev->link->ap->print_id, dev->link->pmp + dev->devno,
&vaf);
va_end(args);
}
EXPORT_SYMBOL(ata_dev_printk);
void ata_print_version(const struct device *dev, const char *version)
{
dev_printk(KERN_DEBUG, dev, "version %s\n", version);
}
EXPORT_SYMBOL(ata_print_version);
/*
* libata is essentially a library of internal helper functions for
* low-level ATA host controller drivers. As such, the API/ABI is
* likely to change as new drivers are added and updated.
* Do not depend on ABI/API stability.
*/
EXPORT_SYMBOL_GPL(sata_deb_timing_normal);
EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug);
EXPORT_SYMBOL_GPL(sata_deb_timing_long);
EXPORT_SYMBOL_GPL(ata_base_port_ops);
EXPORT_SYMBOL_GPL(sata_port_ops);
EXPORT_SYMBOL_GPL(ata_dummy_port_ops);
EXPORT_SYMBOL_GPL(ata_dummy_port_info);
EXPORT_SYMBOL_GPL(ata_link_next);
EXPORT_SYMBOL_GPL(ata_dev_next);
EXPORT_SYMBOL_GPL(ata_std_bios_param);
EXPORT_SYMBOL_GPL(ata_scsi_unlock_native_capacity);
EXPORT_SYMBOL_GPL(ata_host_init);
EXPORT_SYMBOL_GPL(ata_host_alloc);
EXPORT_SYMBOL_GPL(ata_host_alloc_pinfo);
EXPORT_SYMBOL_GPL(ata_slave_link_init);
EXPORT_SYMBOL_GPL(ata_host_start);
EXPORT_SYMBOL_GPL(ata_host_register);
EXPORT_SYMBOL_GPL(ata_host_activate);
EXPORT_SYMBOL_GPL(ata_host_detach);
EXPORT_SYMBOL_GPL(ata_sg_init);
EXPORT_SYMBOL_GPL(ata_qc_complete);
EXPORT_SYMBOL_GPL(ata_qc_complete_multiple);
EXPORT_SYMBOL_GPL(atapi_cmd_type);
EXPORT_SYMBOL_GPL(ata_tf_to_fis);
EXPORT_SYMBOL_GPL(ata_tf_from_fis);
EXPORT_SYMBOL_GPL(ata_pack_xfermask);
EXPORT_SYMBOL_GPL(ata_unpack_xfermask);
EXPORT_SYMBOL_GPL(ata_xfer_mask2mode);
EXPORT_SYMBOL_GPL(ata_xfer_mode2mask);
EXPORT_SYMBOL_GPL(ata_xfer_mode2shift);
EXPORT_SYMBOL_GPL(ata_mode_string);
EXPORT_SYMBOL_GPL(ata_id_xfermask);
EXPORT_SYMBOL_GPL(ata_do_set_mode);
EXPORT_SYMBOL_GPL(ata_std_qc_defer);
EXPORT_SYMBOL_GPL(ata_noop_qc_prep);
EXPORT_SYMBOL_GPL(ata_dev_disable);
EXPORT_SYMBOL_GPL(sata_set_spd);
EXPORT_SYMBOL_GPL(ata_wait_after_reset);
EXPORT_SYMBOL_GPL(sata_link_debounce);
EXPORT_SYMBOL_GPL(sata_link_resume);
EXPORT_SYMBOL_GPL(sata_link_scr_lpm);
EXPORT_SYMBOL_GPL(ata_std_prereset);
EXPORT_SYMBOL_GPL(sata_link_hardreset);
EXPORT_SYMBOL_GPL(sata_std_hardreset);
EXPORT_SYMBOL_GPL(ata_std_postreset);
EXPORT_SYMBOL_GPL(ata_dev_classify);
EXPORT_SYMBOL_GPL(ata_dev_pair);
EXPORT_SYMBOL_GPL(ata_ratelimit);
EXPORT_SYMBOL_GPL(ata_msleep);
EXPORT_SYMBOL_GPL(ata_wait_register);
EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
EXPORT_SYMBOL_GPL(ata_scsi_slave_destroy);
EXPORT_SYMBOL_GPL(ata_scsi_change_queue_depth);
EXPORT_SYMBOL_GPL(__ata_change_queue_depth);
EXPORT_SYMBOL_GPL(sata_scr_valid);
EXPORT_SYMBOL_GPL(sata_scr_read);
EXPORT_SYMBOL_GPL(sata_scr_write);
EXPORT_SYMBOL_GPL(sata_scr_write_flush);
EXPORT_SYMBOL_GPL(ata_link_online);
EXPORT_SYMBOL_GPL(ata_link_offline);
#ifdef CONFIG_PM
EXPORT_SYMBOL_GPL(ata_host_suspend);
EXPORT_SYMBOL_GPL(ata_host_resume);
#endif /* CONFIG_PM */
EXPORT_SYMBOL_GPL(ata_id_string);
EXPORT_SYMBOL_GPL(ata_id_c_string);
EXPORT_SYMBOL_GPL(ata_do_dev_read_id);
EXPORT_SYMBOL_GPL(ata_scsi_simulate);
EXPORT_SYMBOL_GPL(ata_pio_need_iordy);
EXPORT_SYMBOL_GPL(ata_timing_find_mode);
EXPORT_SYMBOL_GPL(ata_timing_compute);
EXPORT_SYMBOL_GPL(ata_timing_merge);
EXPORT_SYMBOL_GPL(ata_timing_cycle2mode);
#ifdef CONFIG_PCI
EXPORT_SYMBOL_GPL(pci_test_config_bits);
EXPORT_SYMBOL_GPL(ata_pci_remove_one);
#ifdef CONFIG_PM
EXPORT_SYMBOL_GPL(ata_pci_device_do_suspend);
EXPORT_SYMBOL_GPL(ata_pci_device_do_resume);
EXPORT_SYMBOL_GPL(ata_pci_device_suspend);
EXPORT_SYMBOL_GPL(ata_pci_device_resume);
#endif /* CONFIG_PM */
#endif /* CONFIG_PCI */
EXPORT_SYMBOL_GPL(ata_platform_remove_one);
EXPORT_SYMBOL_GPL(__ata_ehi_push_desc);
EXPORT_SYMBOL_GPL(ata_ehi_push_desc);
EXPORT_SYMBOL_GPL(ata_ehi_clear_desc);
EXPORT_SYMBOL_GPL(ata_port_desc);
#ifdef CONFIG_PCI
EXPORT_SYMBOL_GPL(ata_port_pbar_desc);
#endif /* CONFIG_PCI */
EXPORT_SYMBOL_GPL(ata_port_schedule_eh);
EXPORT_SYMBOL_GPL(ata_link_abort);
EXPORT_SYMBOL_GPL(ata_port_abort);
EXPORT_SYMBOL_GPL(ata_port_freeze);
EXPORT_SYMBOL_GPL(sata_async_notification);
EXPORT_SYMBOL_GPL(ata_eh_freeze_port);
EXPORT_SYMBOL_GPL(ata_eh_thaw_port);
EXPORT_SYMBOL_GPL(ata_eh_qc_complete);
EXPORT_SYMBOL_GPL(ata_eh_qc_retry);
EXPORT_SYMBOL_GPL(ata_eh_analyze_ncq_error);
EXPORT_SYMBOL_GPL(ata_do_eh);
EXPORT_SYMBOL_GPL(ata_std_error_handler);
EXPORT_SYMBOL_GPL(ata_cable_40wire);
EXPORT_SYMBOL_GPL(ata_cable_80wire);
EXPORT_SYMBOL_GPL(ata_cable_unknown);
EXPORT_SYMBOL_GPL(ata_cable_ignore);
EXPORT_SYMBOL_GPL(ata_cable_sata);
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/ata/libata-eh.c 0000664 0000000 0000000 00000333666 12666203034 0026224 0 ustar 00root root 0000000 0000000 /*
* libata-eh.c - libata error handling
*
* Maintained by: Tejun Heo
* Please ALWAYS copy linux-ide@vger.kernel.org
* on emails.
*
* Copyright 2006 Tejun Heo
*
*
* This program is free software; you can redistribute 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, 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
*
* libata documentation is available via 'make {ps|pdf}docs',
* as Documentation/DocBook/libata.*
*
* Hardware documentation available from http://www.t13.org/ and
* http://www.sata-io.org/
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../scsi/scsi_transport_api.h"
#include
#include "libata.h"
enum {
/* speed down verdicts */
ATA_EH_SPDN_NCQ_OFF = (1 << 0),
ATA_EH_SPDN_SPEED_DOWN = (1 << 1),
ATA_EH_SPDN_FALLBACK_TO_PIO = (1 << 2),
ATA_EH_SPDN_KEEP_ERRORS = (1 << 3),
/* error flags */
ATA_EFLAG_IS_IO = (1 << 0),
ATA_EFLAG_DUBIOUS_XFER = (1 << 1),
ATA_EFLAG_OLD_ER = (1 << 31),
/* error categories */
ATA_ECAT_NONE = 0,
ATA_ECAT_ATA_BUS = 1,
ATA_ECAT_TOUT_HSM = 2,
ATA_ECAT_UNK_DEV = 3,
ATA_ECAT_DUBIOUS_NONE = 4,
ATA_ECAT_DUBIOUS_ATA_BUS = 5,
ATA_ECAT_DUBIOUS_TOUT_HSM = 6,
ATA_ECAT_DUBIOUS_UNK_DEV = 7,
ATA_ECAT_NR = 8,
ATA_EH_CMD_DFL_TIMEOUT = 5000,
/* always put at least this amount of time between resets */
ATA_EH_RESET_COOL_DOWN = 5000,
/* Waiting in ->prereset can never be reliable. It's
* sometimes nice to wait there but it can't be depended upon;
* otherwise, we wouldn't be resetting. Just give it enough
* time for most drives to spin up.
*/
ATA_EH_PRERESET_TIMEOUT = 10000,
ATA_EH_FASTDRAIN_INTERVAL = 3000,
ATA_EH_UA_TRIES = 5,
/* probe speed down parameters, see ata_eh_schedule_probe() */
ATA_EH_PROBE_TRIAL_INTERVAL = 60000, /* 1 min */
ATA_EH_PROBE_TRIALS = 2,
};
/* The following table determines how we sequence resets. Each entry
* represents timeout for that try. The first try can be soft or
* hardreset. All others are hardreset if available. In most cases
* the first reset w/ 10sec timeout should succeed. Following entries
* are mostly for error handling, hotplug and those outlier devices that
* take an exceptionally long time to recover from reset.
*/
static const unsigned long ata_eh_reset_timeouts[] = {
10000, /* most drives spin up by 10sec */
10000, /* > 99% working drives spin up before 20sec */
35000, /* give > 30 secs of idleness for outlier devices */
5000, /* and sweet one last chance */
ULONG_MAX, /* > 1 min has elapsed, give up */
};
static const unsigned long ata_eh_identify_timeouts[] = {
5000, /* covers > 99% of successes and not too boring on failures */
10000, /* combined time till here is enough even for media access */
30000, /* for true idiots */
ULONG_MAX,
};
static const unsigned long ata_eh_flush_timeouts[] = {
15000, /* be generous with flush */
15000, /* ditto */
30000, /* and even more generous */
ULONG_MAX,
};
static const unsigned long ata_eh_other_timeouts[] = {
5000, /* same rationale as identify timeout */
10000, /* ditto */
/* but no merciful 30sec for other commands, it just isn't worth it */
ULONG_MAX,
};
struct ata_eh_cmd_timeout_ent {
const u8 *commands;
const unsigned long *timeouts;
};
/* The following table determines timeouts to use for EH internal
* commands. Each table entry is a command class and matches the
* commands the entry applies to and the timeout table to use.
*
* On the retry after a command timed out, the next timeout value from
* the table is used. If the table doesn't contain further entries,
* the last value is used.
*
* ehc->cmd_timeout_idx keeps track of which timeout to use per
* command class, so if SET_FEATURES times out on the first try, the
* next try will use the second timeout value only for that class.
*/
#define CMDS(cmds...) (const u8 []){ cmds, 0 }
static const struct ata_eh_cmd_timeout_ent
ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = {
{ .commands = CMDS(ATA_CMD_ID_ATA, ATA_CMD_ID_ATAPI),
.timeouts = ata_eh_identify_timeouts, },
{ .commands = CMDS(ATA_CMD_READ_NATIVE_MAX, ATA_CMD_READ_NATIVE_MAX_EXT),
.timeouts = ata_eh_other_timeouts, },
{ .commands = CMDS(ATA_CMD_SET_MAX, ATA_CMD_SET_MAX_EXT),
.timeouts = ata_eh_other_timeouts, },
{ .commands = CMDS(ATA_CMD_SET_FEATURES),
.timeouts = ata_eh_other_timeouts, },
{ .commands = CMDS(ATA_CMD_INIT_DEV_PARAMS),
.timeouts = ata_eh_other_timeouts, },
{ .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT),
.timeouts = ata_eh_flush_timeouts },
};
#undef CMDS
static void __ata_port_freeze(struct ata_port *ap);
#ifdef CONFIG_PM
static void ata_eh_handle_port_suspend(struct ata_port *ap);
static void ata_eh_handle_port_resume(struct ata_port *ap);
#else /* CONFIG_PM */
static void ata_eh_handle_port_suspend(struct ata_port *ap)
{ }
static void ata_eh_handle_port_resume(struct ata_port *ap)
{ }
#endif /* CONFIG_PM */
static void __ata_ehi_pushv_desc(struct ata_eh_info *ehi, const char *fmt,
va_list args)
{
ehi->desc_len += vscnprintf(ehi->desc + ehi->desc_len,
ATA_EH_DESC_LEN - ehi->desc_len,
fmt, args);
}
/**
* __ata_ehi_push_desc - push error description without adding separator
* @ehi: target EHI
* @fmt: printf format string
*
* Format string according to @fmt and append it to @ehi->desc.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
void __ata_ehi_push_desc(struct ata_eh_info *ehi, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
__ata_ehi_pushv_desc(ehi, fmt, args);
va_end(args);
}
/**
* ata_ehi_push_desc - push error description with separator
* @ehi: target EHI
* @fmt: printf format string
*
* Format string according to @fmt and append it to @ehi->desc.
* If @ehi->desc is not empty, ", " is added in-between.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
void ata_ehi_push_desc(struct ata_eh_info *ehi, const char *fmt, ...)
{
va_list args;
if (ehi->desc_len)
__ata_ehi_push_desc(ehi, ", ");
va_start(args, fmt);
__ata_ehi_pushv_desc(ehi, fmt, args);
va_end(args);
}
/**
* ata_ehi_clear_desc - clean error description
* @ehi: target EHI
*
* Clear @ehi->desc.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
void ata_ehi_clear_desc(struct ata_eh_info *ehi)
{
ehi->desc[0] = '\0';
ehi->desc_len = 0;
}
/**
* ata_port_desc - append port description
* @ap: target ATA port
* @fmt: printf format string
*
* Format string according to @fmt and append it to port
* description. If port description is not empty, " " is added
* in-between. This function is to be used while initializing
* ata_host. The description is printed on host registration.
*
* LOCKING:
* None.
*/
void ata_port_desc(struct ata_port *ap, const char *fmt, ...)
{
va_list args;
WARN_ON(!(ap->pflags & ATA_PFLAG_INITIALIZING));
if (ap->link.eh_info.desc_len)
__ata_ehi_push_desc(&ap->link.eh_info, " ");
va_start(args, fmt);
__ata_ehi_pushv_desc(&ap->link.eh_info, fmt, args);
va_end(args);
}
#ifdef CONFIG_PCI
/**
* ata_port_pbar_desc - append PCI BAR description
* @ap: target ATA port
* @bar: target PCI BAR
* @offset: offset into PCI BAR
* @name: name of the area
*
* If @offset is negative, this function formats a string which
* contains the name, address, size and type of the BAR and
* appends it to the port description. If @offset is zero or
* positive, only name and offsetted address is appended.
*
* LOCKING:
* None.
*/
void ata_port_pbar_desc(struct ata_port *ap, int bar, ssize_t offset,
const char *name)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
char *type = "";
unsigned long long start, len;
if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM)
type = "m";
else if (pci_resource_flags(pdev, bar) & IORESOURCE_IO)
type = "i";
start = (unsigned long long)pci_resource_start(pdev, bar);
len = (unsigned long long)pci_resource_len(pdev, bar);
if (offset < 0)
ata_port_desc(ap, "%s %s%llu@0x%llx", name, type, len, start);
else
ata_port_desc(ap, "%s 0x%llx", name,
start + (unsigned long long)offset);
}
#endif /* CONFIG_PCI */
static int ata_lookup_timeout_table(u8 cmd)
{
int i;
for (i = 0; i < ATA_EH_CMD_TIMEOUT_TABLE_SIZE; i++) {
const u8 *cur;
for (cur = ata_eh_cmd_timeout_table[i].commands; *cur; cur++)
if (*cur == cmd)
return i;
}
return -1;
}
/**
* ata_internal_cmd_timeout - determine timeout for an internal command
* @dev: target device
* @cmd: internal command to be issued
*
* Determine timeout for internal command @cmd for @dev.
*
* LOCKING:
* EH context.
*
* RETURNS:
* Determined timeout.
*/
unsigned long ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd)
{
struct ata_eh_context *ehc = &dev->link->eh_context;
int ent = ata_lookup_timeout_table(cmd);
int idx;
if (ent < 0)
return ATA_EH_CMD_DFL_TIMEOUT;
idx = ehc->cmd_timeout_idx[dev->devno][ent];
return ata_eh_cmd_timeout_table[ent].timeouts[idx];
}
/**
* ata_internal_cmd_timed_out - notification for internal command timeout
* @dev: target device
* @cmd: internal command which timed out
*
* Notify EH that internal command @cmd for @dev timed out. This
* function should be called only for commands whose timeouts are
* determined using ata_internal_cmd_timeout().
*
* LOCKING:
* EH context.
*/
void ata_internal_cmd_timed_out(struct ata_device *dev, u8 cmd)
{
struct ata_eh_context *ehc = &dev->link->eh_context;
int ent = ata_lookup_timeout_table(cmd);
int idx;
if (ent < 0)
return;
idx = ehc->cmd_timeout_idx[dev->devno][ent];
if (ata_eh_cmd_timeout_table[ent].timeouts[idx + 1] != ULONG_MAX)
ehc->cmd_timeout_idx[dev->devno][ent]++;
}
static void ata_ering_record(struct ata_ering *ering, unsigned int eflags,
unsigned int err_mask)
{
struct ata_ering_entry *ent;
WARN_ON(!err_mask);
ering->cursor++;
ering->cursor %= ATA_ERING_SIZE;
ent = &ering->ring[ering->cursor];
ent->eflags = eflags;
ent->err_mask = err_mask;
ent->timestamp = get_jiffies_64();
}
static struct ata_ering_entry *ata_ering_top(struct ata_ering *ering)
{
struct ata_ering_entry *ent = &ering->ring[ering->cursor];
if (ent->err_mask)
return ent;
return NULL;
}
int ata_ering_map(struct ata_ering *ering,
int (*map_fn)(struct ata_ering_entry *, void *),
void *arg)
{
int idx, rc = 0;
struct ata_ering_entry *ent;
idx = ering->cursor;
do {
ent = &ering->ring[idx];
if (!ent->err_mask)
break;
rc = map_fn(ent, arg);
if (rc)
break;
idx = (idx - 1 + ATA_ERING_SIZE) % ATA_ERING_SIZE;
} while (idx != ering->cursor);
return rc;
}
static int ata_ering_clear_cb(struct ata_ering_entry *ent, void *void_arg)
{
ent->eflags |= ATA_EFLAG_OLD_ER;
return 0;
}
static void ata_ering_clear(struct ata_ering *ering)
{
ata_ering_map(ering, ata_ering_clear_cb, NULL);
}
static unsigned int ata_eh_dev_action(struct ata_device *dev)
{
struct ata_eh_context *ehc = &dev->link->eh_context;
return ehc->i.action | ehc->i.dev_action[dev->devno];
}
static void ata_eh_clear_action(struct ata_link *link, struct ata_device *dev,
struct ata_eh_info *ehi, unsigned int action)
{
struct ata_device *tdev;
if (!dev) {
ehi->action &= ~action;
ata_for_each_dev(tdev, link, ALL)
ehi->dev_action[tdev->devno] &= ~action;
} else {
/* doesn't make sense for port-wide EH actions */
WARN_ON(!(action & ATA_EH_PERDEV_MASK));
/* break ehi->action into ehi->dev_action */
if (ehi->action & action) {
ata_for_each_dev(tdev, link, ALL)
ehi->dev_action[tdev->devno] |=
ehi->action & action;
ehi->action &= ~action;
}
/* turn off the specified per-dev action */
ehi->dev_action[dev->devno] &= ~action;
}
}
/**
* ata_eh_acquire - acquire EH ownership
* @ap: ATA port to acquire EH ownership for
*
* Acquire EH ownership for @ap. This is the basic exclusion
* mechanism for ports sharing a host. Only one port hanging off
* the same host can claim the ownership of EH.
*
* LOCKING:
* EH context.
*/
void ata_eh_acquire(struct ata_port *ap)
{
mutex_lock(&ap->host->eh_mutex);
WARN_ON_ONCE(ap->host->eh_owner);
ap->host->eh_owner = current;
}
/**
* ata_eh_release - release EH ownership
* @ap: ATA port to release EH ownership for
*
* Release EH ownership for @ap if the caller. The caller must
* have acquired EH ownership using ata_eh_acquire() previously.
*
* LOCKING:
* EH context.
*/
void ata_eh_release(struct ata_port *ap)
{
WARN_ON_ONCE(ap->host->eh_owner != current);
ap->host->eh_owner = NULL;
mutex_unlock(&ap->host->eh_mutex);
}
/**
* ata_scsi_timed_out - SCSI layer time out callback
* @cmd: timed out SCSI command
*
* Handles SCSI layer timeout. We race with normal completion of
* the qc for @cmd. If the qc is already gone, we lose and let
* the scsi command finish (EH_HANDLED). Otherwise, the qc has
* timed out and EH should be invoked. Prevent ata_qc_complete()
* from finishing it by setting EH_SCHEDULED and return
* EH_NOT_HANDLED.
*
* TODO: kill this function once old EH is gone.
*
* LOCKING:
* Called from timer context
*
* RETURNS:
* EH_HANDLED or EH_NOT_HANDLED
*/
enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd)
{
struct Scsi_Host *host = cmd->device->host;
struct ata_port *ap = ata_shost_to_port(host);
unsigned long flags;
struct ata_queued_cmd *qc;
enum blk_eh_timer_return ret;
DPRINTK("ENTER\n");
if (ap->ops->error_handler) {
ret = BLK_EH_NOT_HANDLED;
goto out;
}
ret = BLK_EH_HANDLED;
spin_lock_irqsave(ap->lock, flags);
qc = ata_qc_from_tag(ap, ap->link.active_tag);
if (qc) {
WARN_ON(qc->scsicmd != cmd);
qc->flags |= ATA_QCFLAG_EH_SCHEDULED;
qc->err_mask |= AC_ERR_TIMEOUT;
ret = BLK_EH_NOT_HANDLED;
}
spin_unlock_irqrestore(ap->lock, flags);
out:
DPRINTK("EXIT, ret=%d\n", ret);
return ret;
}
static void ata_eh_unload(struct ata_port *ap)
{
struct ata_link *link;
struct ata_device *dev;
unsigned long flags;
/* Restore SControl IPM and SPD for the next driver and
* disable attached devices.
*/
ata_for_each_link(link, ap, PMP_FIRST) {
sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0);
ata_for_each_dev(dev, link, ALL)
ata_dev_disable(dev);
}
/* freeze and set UNLOADED */
spin_lock_irqsave(ap->lock, flags);
ata_port_freeze(ap); /* won't be thawed */
ap->pflags &= ~ATA_PFLAG_EH_PENDING; /* clear pending from freeze */
ap->pflags |= ATA_PFLAG_UNLOADED;
spin_unlock_irqrestore(ap->lock, flags);
}
/**
* ata_scsi_error - SCSI layer error handler callback
* @host: SCSI host on which error occurred
*
* Handles SCSI-layer-thrown error events.
*
* LOCKING:
* Inherited from SCSI layer (none, can sleep)
*
* RETURNS:
* Zero.
*/
void ata_scsi_error(struct Scsi_Host *host)
{
struct ata_port *ap = ata_shost_to_port(host);
unsigned long flags;
LIST_HEAD(eh_work_q);
DPRINTK("ENTER\n");
spin_lock_irqsave(host->host_lock, flags);
list_splice_init(&host->eh_cmd_q, &eh_work_q);
spin_unlock_irqrestore(host->host_lock, flags);
ata_scsi_cmd_error_handler(host, ap, &eh_work_q);
/* If we timed raced normal completion and there is nothing to
recover nr_timedout == 0 why exactly are we doing error recovery ? */
ata_scsi_port_error_handler(host, ap);
/* finish or retry handled scmd's and clean up */
WARN_ON(host->host_failed || !list_empty(&eh_work_q));
DPRINTK("EXIT\n");
}
/**
* ata_scsi_cmd_error_handler - error callback for a list of commands
* @host: scsi host containing the port
* @ap: ATA port within the host
* @eh_work_q: list of commands to process
*
* process the given list of commands and return those finished to the
* ap->eh_done_q. This function is the first part of the libata error
* handler which processes a given list of failed commands.
*/
void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,
struct list_head *eh_work_q)
{
dev_info(ap->host->dev, "ata_scsi_cmd_error_handler()\n");
int i;
unsigned long flags;
/* make sure sff pio task is not running */
ata_sff_flush_pio_task(ap);
/* synchronize with host lock and sort out timeouts */
/* For new EH, all qcs are finished in one of three ways -
* normal completion, error completion, and SCSI timeout.
* Both completions can race against SCSI timeout. When normal
* completion wins, the qc never reaches EH. When error
* completion wins, the qc has ATA_QCFLAG_FAILED set.
*
* When SCSI timeout wins, things are a bit more complex.
* Normal or error completion can occur after the timeout but
* before this point. In such cases, both types of
* completions are honored. A scmd is determined to have
* timed out iff its associated qc is active and not failed.
*/
if (ap->ops->error_handler) {
struct scsi_cmnd *scmd, *tmp;
int nr_timedout = 0;
spin_lock_irqsave(ap->lock, flags);
/* This must occur under the ap->lock as we don't want
a polled recovery to race the real interrupt handler
The lost_interrupt handler checks for any completed but
non-notified command and completes much like an IRQ handler.
We then fall into the error recovery code which will treat
this as if normal completion won the race */
if (ap->ops->lost_interrupt)
ap->ops->lost_interrupt(ap);
list_for_each_entry_safe(scmd, tmp, eh_work_q, eh_entry) {
struct ata_queued_cmd *qc;
for (i = 0; i < ATA_MAX_QUEUE; i++) {
qc = __ata_qc_from_tag(ap, i);
if (qc->flags & ATA_QCFLAG_ACTIVE &&
qc->scsicmd == scmd)
break;
}
if (i < ATA_MAX_QUEUE) {
/* the scmd has an associated qc */
if (!(qc->flags & ATA_QCFLAG_FAILED)) {
/* which hasn't failed yet, timeout */
qc->err_mask |= AC_ERR_TIMEOUT;
qc->flags |= ATA_QCFLAG_FAILED;
nr_timedout++;
}
} else {
/* Normal completion occurred after
* SCSI timeout but before this point.
* Successfully complete it.
*/
scmd->retries = scmd->allowed;
scsi_eh_finish_cmd(scmd, &ap->eh_done_q);
}
}
/* If we have timed out qcs. They belong to EH from
* this point but the state of the controller is
* unknown. Freeze the port to make sure the IRQ
* handler doesn't diddle with those qcs. This must
* be done atomically w.r.t. setting QCFLAG_FAILED.
*/
if (nr_timedout)
__ata_port_freeze(ap);
spin_unlock_irqrestore(ap->lock, flags);
/* initialize eh_tries */
ap->eh_tries = ATA_EH_MAX_TRIES;
} else
spin_unlock_wait(ap->lock);
}
EXPORT_SYMBOL(ata_scsi_cmd_error_handler);
/**
* ata_scsi_port_error_handler - recover the port after the commands
* @host: SCSI host containing the port
* @ap: the ATA port
*
* Handle the recovery of the port @ap after all the commands
* have been recovered.
*/
void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap)
{
unsigned long flags;
/* invoke error handler */
if (ap->ops->error_handler) {
struct ata_link *link;
/* acquire EH ownership */
ata_eh_acquire(ap);
repeat:
/* kill fast drain timer */
del_timer_sync(&ap->fastdrain_timer);
/* process port resume request */
ata_eh_handle_port_resume(ap);
/* fetch & clear EH info */
spin_lock_irqsave(ap->lock, flags);
ata_for_each_link(link, ap, HOST_FIRST) {
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev;
memset(&link->eh_context, 0, sizeof(link->eh_context));
link->eh_context.i = link->eh_info;
memset(&link->eh_info, 0, sizeof(link->eh_info));
ata_for_each_dev(dev, link, ENABLED) {
int devno = dev->devno;
ehc->saved_xfer_mode[devno] = dev->xfer_mode;
if (ata_ncq_enabled(dev))
ehc->saved_ncq_enabled |= 1 << devno;
}
}
ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
ap->pflags &= ~ATA_PFLAG_EH_PENDING;
ap->excl_link = NULL; /* don't maintain exclusion over EH */
spin_unlock_irqrestore(ap->lock, flags);
/* invoke EH, skip if unloading or suspended */
if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED)))
ap->ops->error_handler(ap);
else {
/* if unloading, commence suicide */
if ((ap->pflags & ATA_PFLAG_UNLOADING) &&
!(ap->pflags & ATA_PFLAG_UNLOADED))
ata_eh_unload(ap);
ata_eh_finish(ap);
}
/* process port suspend request */
ata_eh_handle_port_suspend(ap);
/* Exception might have happened after ->error_handler
* recovered the port but before this point. Repeat
* EH in such case.
*/
spin_lock_irqsave(ap->lock, flags);
if (ap->pflags & ATA_PFLAG_EH_PENDING) {
if (--ap->eh_tries) {
spin_unlock_irqrestore(ap->lock, flags);
goto repeat;
}
ata_port_err(ap,
"EH pending after %d tries, giving up\n",
ATA_EH_MAX_TRIES);
ap->pflags &= ~ATA_PFLAG_EH_PENDING;
}
/* this run is complete, make sure EH info is clear */
ata_for_each_link(link, ap, HOST_FIRST)
memset(&link->eh_info, 0, sizeof(link->eh_info));
/* end eh (clear host_eh_scheduled) while holding
* ap->lock such that if exception occurs after this
* point but before EH completion, SCSI midlayer will
* re-initiate EH.
*/
ap->ops->end_eh(ap);
spin_unlock_irqrestore(ap->lock, flags);
ata_eh_release(ap);
} else {
WARN_ON(ata_qc_from_tag(ap, ap->link.active_tag) == NULL);
ap->ops->eng_timeout(ap);
}
scsi_eh_flush_done_q(&ap->eh_done_q);
/* clean up */
spin_lock_irqsave(ap->lock, flags);
if (ap->pflags & ATA_PFLAG_LOADING)
ap->pflags &= ~ATA_PFLAG_LOADING;
else if (ap->pflags & ATA_PFLAG_SCSI_HOTPLUG)
schedule_delayed_work(&ap->hotplug_task, 0);
if (ap->pflags & ATA_PFLAG_RECOVERED)
ata_port_info(ap, "EH complete\n");
ap->pflags &= ~(ATA_PFLAG_SCSI_HOTPLUG | ATA_PFLAG_RECOVERED);
/* tell wait_eh that we're done */
ap->pflags &= ~ATA_PFLAG_EH_IN_PROGRESS;
wake_up_all(&ap->eh_wait_q);
spin_unlock_irqrestore(ap->lock, flags);
}
EXPORT_SYMBOL_GPL(ata_scsi_port_error_handler);
/**
* ata_port_wait_eh - Wait for the currently pending EH to complete
* @ap: Port to wait EH for
*
* Wait until the currently pending EH is complete.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
void ata_port_wait_eh(struct ata_port *ap)
{
unsigned long flags;
DEFINE_WAIT(wait);
retry:
spin_lock_irqsave(ap->lock, flags);
while (ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS)) {
prepare_to_wait(&ap->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE);
spin_unlock_irqrestore(ap->lock, flags);
schedule();
spin_lock_irqsave(ap->lock, flags);
}
finish_wait(&ap->eh_wait_q, &wait);
spin_unlock_irqrestore(ap->lock, flags);
/* make sure SCSI EH is complete */
if (scsi_host_in_recovery(ap->scsi_host)) {
ata_msleep(ap, 10);
goto retry;
}
}
EXPORT_SYMBOL_GPL(ata_port_wait_eh);
static int ata_eh_nr_in_flight(struct ata_port *ap)
{
unsigned int tag;
int nr = 0;
/* count only non-internal commands */
for (tag = 0; tag < ATA_MAX_QUEUE - 1; tag++)
if (ata_qc_from_tag(ap, tag))
nr++;
return nr;
}
void ata_eh_fastdrain_timerfn(unsigned long arg)
{
struct ata_port *ap = (void *)arg;
unsigned long flags;
int cnt;
spin_lock_irqsave(ap->lock, flags);
cnt = ata_eh_nr_in_flight(ap);
/* are we done? */
if (!cnt)
goto out_unlock;
if (cnt == ap->fastdrain_cnt) {
unsigned int tag;
/* No progress during the last interval, tag all
* in-flight qcs as timed out and freeze the port.
*/
for (tag = 0; tag < ATA_MAX_QUEUE - 1; tag++) {
struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag);
if (qc)
qc->err_mask |= AC_ERR_TIMEOUT;
}
ata_port_freeze(ap);
} else {
/* some qcs have finished, give it another chance */
ap->fastdrain_cnt = cnt;
ap->fastdrain_timer.expires =
ata_deadline(jiffies, ATA_EH_FASTDRAIN_INTERVAL);
add_timer(&ap->fastdrain_timer);
}
out_unlock:
spin_unlock_irqrestore(ap->lock, flags);
}
/**
* ata_eh_set_pending - set ATA_PFLAG_EH_PENDING and activate fast drain
* @ap: target ATA port
* @fastdrain: activate fast drain
*
* Set ATA_PFLAG_EH_PENDING and activate fast drain if @fastdrain
* is non-zero and EH wasn't pending before. Fast drain ensures
* that EH kicks in in timely manner.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
static void ata_eh_set_pending(struct ata_port *ap, int fastdrain)
{
int cnt;
/* already scheduled? */
if (ap->pflags & ATA_PFLAG_EH_PENDING)
return;
ap->pflags |= ATA_PFLAG_EH_PENDING;
if (!fastdrain)
return;
/* do we have in-flight qcs? */
cnt = ata_eh_nr_in_flight(ap);
if (!cnt)
return;
/* activate fast drain */
ap->fastdrain_cnt = cnt;
ap->fastdrain_timer.expires =
ata_deadline(jiffies, ATA_EH_FASTDRAIN_INTERVAL);
add_timer(&ap->fastdrain_timer);
}
/**
* ata_qc_schedule_eh - schedule qc for error handling
* @qc: command to schedule error handling for
*
* Schedule error handling for @qc. EH will kick in as soon as
* other commands are drained.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
void ata_qc_schedule_eh(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct request_queue *q = qc->scsicmd->device->request_queue;
unsigned long flags;
WARN_ON(!ap->ops->error_handler);
qc->flags |= ATA_QCFLAG_FAILED;
ata_eh_set_pending(ap, 1);
/* The following will fail if timeout has already expired.
* ata_scsi_error() takes care of such scmds on EH entry.
* Note that ATA_QCFLAG_FAILED is unconditionally set after
* this function completes.
*/
spin_lock_irqsave(q->queue_lock, flags);
blk_abort_request(qc->scsicmd->request);
spin_unlock_irqrestore(q->queue_lock, flags);
}
/**
* ata_std_sched_eh - non-libsas ata_ports issue eh with this common routine
* @ap: ATA port to schedule EH for
*
* LOCKING: inherited from ata_port_schedule_eh
* spin_lock_irqsave(host lock)
*/
void ata_std_sched_eh(struct ata_port *ap)
{
WARN_ON(!ap->ops->error_handler);
if (ap->pflags & ATA_PFLAG_INITIALIZING)
return;
ata_eh_set_pending(ap, 1);
scsi_schedule_eh(ap->scsi_host);
DPRINTK("port EH scheduled\n");
}
EXPORT_SYMBOL_GPL(ata_std_sched_eh);
/**
* ata_std_end_eh - non-libsas ata_ports complete eh with this common routine
* @ap: ATA port to end EH for
*
* In the libata object model there is a 1:1 mapping of ata_port to
* shost, so host fields can be directly manipulated under ap->lock, in
* the libsas case we need to hold a lock at the ha->level to coordinate
* these events.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
void ata_std_end_eh(struct ata_port *ap)
{
struct Scsi_Host *host = ap->scsi_host;
host->host_eh_scheduled = 0;
}
EXPORT_SYMBOL(ata_std_end_eh);
/**
* ata_port_schedule_eh - schedule error handling without a qc
* @ap: ATA port to schedule EH for
*
* Schedule error handling for @ap. EH will kick in as soon as
* all commands are drained.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
void ata_port_schedule_eh(struct ata_port *ap)
{
/* see: ata_std_sched_eh, unless you know better */
ap->ops->sched_eh(ap);
}
static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link)
{
int tag, nr_aborted = 0;
WARN_ON(!ap->ops->error_handler);
/* we're gonna abort all commands, no need for fast drain */
ata_eh_set_pending(ap, 0);
for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag);
if (qc && (!link || qc->dev->link == link)) {
qc->flags |= ATA_QCFLAG_FAILED;
ata_qc_complete(qc);
nr_aborted++;
}
}
if (!nr_aborted)
ata_port_schedule_eh(ap);
return nr_aborted;
}
/**
* ata_link_abort - abort all qc's on the link
* @link: ATA link to abort qc's for
*
* Abort all active qc's active on @link and schedule EH.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*
* RETURNS:
* Number of aborted qc's.
*/
int ata_link_abort(struct ata_link *link)
{
return ata_do_link_abort(link->ap, link);
}
/**
* ata_port_abort - abort all qc's on the port
* @ap: ATA port to abort qc's for
*
* Abort all active qc's of @ap and schedule EH.
*
* LOCKING:
* spin_lock_irqsave(host_set lock)
*
* RETURNS:
* Number of aborted qc's.
*/
int ata_port_abort(struct ata_port *ap)
{
return ata_do_link_abort(ap, NULL);
}
/**
* __ata_port_freeze - freeze port
* @ap: ATA port to freeze
*
* This function is called when HSM violation or some other
* condition disrupts normal operation of the port. Frozen port
* is not allowed to perform any operation until the port is
* thawed, which usually follows a successful reset.
*
* ap->ops->freeze() callback can be used for freezing the port
* hardware-wise (e.g. mask interrupt and stop DMA engine). If a
* port cannot be frozen hardware-wise, the interrupt handler
* must ack and clear interrupts unconditionally while the port
* is frozen.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*/
static void __ata_port_freeze(struct ata_port *ap)
{
WARN_ON(!ap->ops->error_handler);
dev_info(ap->host->dev, "__ata_port_freeze()\n");
if (ap->ops->freeze)
ap->ops->freeze(ap);
ap->pflags |= ATA_PFLAG_FROZEN;
DPRINTK("ata%u port frozen\n", ap->print_id);
}
/**
* ata_port_freeze - abort & freeze port
* @ap: ATA port to freeze
*
* Abort and freeze @ap. The freeze operation must be called
* first, because some hardware requires special operations
* before the taskfile registers are accessible.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*
* RETURNS:
* Number of aborted commands.
*/
int ata_port_freeze(struct ata_port *ap)
{
int nr_aborted;
WARN_ON(!ap->ops->error_handler);
dev_info(ap->host->dev, "libata-eh.c: ahci_port_freeze()\n");
__ata_port_freeze(ap);
nr_aborted = ata_port_abort(ap);
return nr_aborted;
}
/**
* sata_async_notification - SATA async notification handler
* @ap: ATA port where async notification is received
*
* Handler to be called when async notification via SDB FIS is
* received. This function schedules EH if necessary.
*
* LOCKING:
* spin_lock_irqsave(host lock)
*
* RETURNS:
* 1 if EH is scheduled, 0 otherwise.
*/
int sata_async_notification(struct ata_port *ap)
{
u32 sntf;
int rc;
if (!(ap->flags & ATA_FLAG_AN))
return 0;
rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf);
if (rc == 0)
sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf);
if (!sata_pmp_attached(ap) || rc) {
/* PMP is not attached or SNTF is not available */
if (!sata_pmp_attached(ap)) {
/* PMP is not attached. Check whether ATAPI
* AN is configured. If so, notify media
* change.
*/
struct ata_device *dev = ap->link.device;
if ((dev->class == ATA_DEV_ATAPI) &&
(dev->flags & ATA_DFLAG_AN))
ata_scsi_media_change_notify(dev);
return 0;
} else {
/* PMP is attached but SNTF is not available.
* ATAPI async media change notification is
* not used. The PMP must be reporting PHY
* status change, schedule EH.
*/
ata_port_schedule_eh(ap);
return 1;
}
} else {
/* PMP is attached and SNTF is available */
struct ata_link *link;
/* check and notify ATAPI AN */
ata_for_each_link(link, ap, EDGE) {
if (!(sntf & (1 << link->pmp)))
continue;
if ((link->device->class == ATA_DEV_ATAPI) &&
(link->device->flags & ATA_DFLAG_AN))
ata_scsi_media_change_notify(link->device);
}
/* If PMP is reporting that PHY status of some
* downstream ports has changed, schedule EH.
*/
if (sntf & (1 << SATA_PMP_CTRL_PORT)) {
ata_port_schedule_eh(ap);
return 1;
}
return 0;
}
}
/**
* ata_eh_freeze_port - EH helper to freeze port
* @ap: ATA port to freeze
*
* Freeze @ap.
*
* LOCKING:
* None.
*/
void ata_eh_freeze_port(struct ata_port *ap)
{
unsigned long flags;
dev_info(ap->host->dev, "ata_eh_freeze_port()\n");
if (!ap->ops->error_handler)
return;
spin_lock_irqsave(ap->lock, flags);
__ata_port_freeze(ap);
spin_unlock_irqrestore(ap->lock, flags);
}
/**
* ata_port_thaw_port - EH helper to thaw port
* @ap: ATA port to thaw
*
* Thaw frozen port @ap.
*
* LOCKING:
* None.
*/
void ata_eh_thaw_port(struct ata_port *ap)
{
unsigned long flags;
if (!ap->ops->error_handler)
return;
spin_lock_irqsave(ap->lock, flags);
ap->pflags &= ~ATA_PFLAG_FROZEN;
if (ap->ops->thaw)
ap->ops->thaw(ap);
spin_unlock_irqrestore(ap->lock, flags);
DPRINTK("ata%u port thawed\n", ap->print_id);
}
static void ata_eh_scsidone(struct scsi_cmnd *scmd)
{
/* nada */
}
static void __ata_eh_qc_complete(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct scsi_cmnd *scmd = qc->scsicmd;
unsigned long flags;
spin_lock_irqsave(ap->lock, flags);
qc->scsidone = ata_eh_scsidone;
__ata_qc_complete(qc);
WARN_ON(ata_tag_valid(qc->tag));
spin_unlock_irqrestore(ap->lock, flags);
scsi_eh_finish_cmd(scmd, &ap->eh_done_q);
}
/**
* ata_eh_qc_complete - Complete an active ATA command from EH
* @qc: Command to complete
*
* Indicate to the mid and upper layers that an ATA command has
* completed. To be used from EH.
*/
void ata_eh_qc_complete(struct ata_queued_cmd *qc)
{
struct scsi_cmnd *scmd = qc->scsicmd;
scmd->retries = scmd->allowed;
__ata_eh_qc_complete(qc);
}
/**
* ata_eh_qc_retry - Tell midlayer to retry an ATA command after EH
* @qc: Command to retry
*
* Indicate to the mid and upper layers that an ATA command
* should be retried. To be used from EH.
*
* SCSI midlayer limits the number of retries to scmd->allowed.
* scmd->allowed is incremented for commands which get retried
* due to unrelated failures (qc->err_mask is zero).
*/
void ata_eh_qc_retry(struct ata_queued_cmd *qc)
{
struct scsi_cmnd *scmd = qc->scsicmd;
if (!qc->err_mask)
scmd->allowed++;
__ata_eh_qc_complete(qc);
}
/**
* ata_dev_disable - disable ATA device
* @dev: ATA device to disable
*
* Disable @dev.
*
* Locking:
* EH context.
*/
void ata_dev_disable(struct ata_device *dev)
{
if (!ata_dev_enabled(dev))
return;
if (ata_msg_drv(dev->link->ap))
ata_dev_warn(dev, "disabled\n");
ata_acpi_on_disable(dev);
ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO0 | ATA_DNXFER_QUIET);
dev->class++;
/* From now till the next successful probe, ering is used to
* track probe failures. Clear accumulated device error info.
*/
ata_ering_clear(&dev->ering);
}
/**
* ata_eh_detach_dev - detach ATA device
* @dev: ATA device to detach
*
* Detach @dev.
*
* LOCKING:
* None.
*/
void ata_eh_detach_dev(struct ata_device *dev)
{
struct ata_link *link = dev->link;
struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
unsigned long flags;
ata_dev_disable(dev);
spin_lock_irqsave(ap->lock, flags);
dev->flags &= ~ATA_DFLAG_DETACH;
if (ata_scsi_offline_dev(dev)) {
dev->flags |= ATA_DFLAG_DETACHED;
ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG;
}
/* clear per-dev EH info */
ata_eh_clear_action(link, dev, &link->eh_info, ATA_EH_PERDEV_MASK);
ata_eh_clear_action(link, dev, &link->eh_context.i, ATA_EH_PERDEV_MASK);
ehc->saved_xfer_mode[dev->devno] = 0;
ehc->saved_ncq_enabled &= ~(1 << dev->devno);
spin_unlock_irqrestore(ap->lock, flags);
}
/**
* ata_eh_about_to_do - about to perform eh_action
* @link: target ATA link
* @dev: target ATA dev for per-dev action (can be NULL)
* @action: action about to be performed
*
* Called just before performing EH actions to clear related bits
* in @link->eh_info such that eh actions are not unnecessarily
* repeated.
*
* LOCKING:
* None.
*/
void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
unsigned int action)
{
struct ata_port *ap = link->ap;
struct ata_eh_info *ehi = &link->eh_info;
struct ata_eh_context *ehc = &link->eh_context;
unsigned long flags;
spin_lock_irqsave(ap->lock, flags);
ata_eh_clear_action(link, dev, ehi, action);
/* About to take EH action, set RECOVERED. Ignore actions on
* slave links as master will do them again.
*/
if (!(ehc->i.flags & ATA_EHI_QUIET) && link != ap->slave_link)
ap->pflags |= ATA_PFLAG_RECOVERED;
spin_unlock_irqrestore(ap->lock, flags);
}
/**
* ata_eh_done - EH action complete
* @ap: target ATA port
* @dev: target ATA dev for per-dev action (can be NULL)
* @action: action just completed
*
* Called right after performing EH actions to clear related bits
* in @link->eh_context.
*
* LOCKING:
* None.
*/
void ata_eh_done(struct ata_link *link, struct ata_device *dev,
unsigned int action)
{
struct ata_eh_context *ehc = &link->eh_context;
ata_eh_clear_action(link, dev, &ehc->i, action);
}
/**
* ata_err_string - convert err_mask to descriptive string
* @err_mask: error mask to convert to string
*
* Convert @err_mask to descriptive string. Errors are
* prioritized according to severity and only the most severe
* error is reported.
*
* LOCKING:
* None.
*
* RETURNS:
* Descriptive string for @err_mask
*/
static const char *ata_err_string(unsigned int err_mask)
{
if (err_mask & AC_ERR_HOST_BUS)
return "host bus error";
if (err_mask & AC_ERR_ATA_BUS)
return "ATA bus error";
if (err_mask & AC_ERR_TIMEOUT)
return "timeout";
if (err_mask & AC_ERR_HSM)
return "HSM violation";
if (err_mask & AC_ERR_SYSTEM)
return "internal error";
if (err_mask & AC_ERR_MEDIA)
return "media error";
if (err_mask & AC_ERR_INVALID)
return "invalid argument";
if (err_mask & AC_ERR_DEV)
return "device error";
return "unknown error";
}
/**
* ata_read_log_page - read a specific log page
* @dev: target device
* @log: log to read
* @page: page to read
* @buf: buffer to store read page
* @sectors: number of sectors to read
*
* Read log page using READ_LOG_EXT command.
*
* LOCKING:
* Kernel thread context (may sleep).
*
* RETURNS:
* 0 on success, AC_ERR_* mask otherwise.
*/
unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
u8 page, void *buf, unsigned int sectors)
{
struct ata_taskfile tf;
unsigned int err_mask;
DPRINTK("read log page - log 0x%x, page 0x%x\n", log, page);
ata_tf_init(dev, &tf);
tf.command = ATA_CMD_READ_LOG_EXT;
tf.lbal = log;
tf.lbam = page;
tf.nsect = sectors;
tf.hob_nsect = sectors >> 8;
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_LBA48 | ATA_TFLAG_DEVICE;
tf.protocol = ATA_PROT_PIO;
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE,
buf, sectors * ATA_SECT_SIZE, 0);
DPRINTK("EXIT, err_mask=%x\n", err_mask);
return err_mask;
}
/**
* ata_eh_read_log_10h - Read log page 10h for NCQ error details
* @dev: Device to read log page 10h from
* @tag: Resulting tag of the failed command
* @tf: Resulting taskfile registers of the failed command
*
* Read log page 10h to obtain NCQ error details and clear error
* condition.
*
* LOCKING:
* Kernel thread context (may sleep).
*
* RETURNS:
* 0 on success, -errno otherwise.
*/
static int ata_eh_read_log_10h(struct ata_device *dev,
int *tag, struct ata_taskfile *tf)
{
u8 *buf = dev->link->ap->sector_buf;
unsigned int err_mask;
u8 csum;
int i;
err_mask = ata_read_log_page(dev, ATA_LOG_SATA_NCQ, 0, buf, 1);
if (err_mask)
return -EIO;
csum = 0;
for (i = 0; i < ATA_SECT_SIZE; i++)
csum += buf[i];
if (csum)
ata_dev_warn(dev, "invalid checksum 0x%x on log page 10h\n",
csum);
if (buf[0] & 0x80)
return -ENOENT;
*tag = buf[0] & 0x1f;
tf->command = buf[2];
tf->feature = buf[3];
tf->lbal = buf[4];
tf->lbam = buf[5];
tf->lbah = buf[6];
tf->device = buf[7];
tf->hob_lbal = buf[8];
tf->hob_lbam = buf[9];
tf->hob_lbah = buf[10];
tf->nsect = buf[12];
tf->hob_nsect = buf[13];
return 0;
}
/**
* atapi_eh_tur - perform ATAPI TEST_UNIT_READY
* @dev: target ATAPI device
* @r_sense_key: out parameter for sense_key
*
* Perform ATAPI TEST_UNIT_READY.
*
* LOCKING:
* EH context (may sleep).
*
* RETURNS:
* 0 on success, AC_ERR_* mask on failure.
*/
unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key)
{
u8 cdb[ATAPI_CDB_LEN] = { TEST_UNIT_READY, 0, 0, 0, 0, 0 };
struct ata_taskfile tf;
unsigned int err_mask;
ata_tf_init(dev, &tf);
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf.command = ATA_CMD_PACKET;
tf.protocol = ATAPI_PROT_NODATA;
err_mask = ata_exec_internal(dev, &tf, cdb, DMA_NONE, NULL, 0, 0);
if (err_mask == AC_ERR_DEV)
*r_sense_key = tf.feature >> 4;
return err_mask;
}
/**
* atapi_eh_request_sense - perform ATAPI REQUEST_SENSE
* @dev: device to perform REQUEST_SENSE to
* @sense_buf: result sense data buffer (SCSI_SENSE_BUFFERSIZE bytes long)
* @dfl_sense_key: default sense key to use
*
* Perform ATAPI REQUEST_SENSE after the device reported CHECK
* SENSE. This function is EH helper.
*
* LOCKING:
* Kernel thread context (may sleep).
*
* RETURNS:
* 0 on success, AC_ERR_* mask on failure
*/
unsigned int atapi_eh_request_sense(struct ata_device *dev,
u8 *sense_buf, u8 dfl_sense_key)
{
u8 cdb[ATAPI_CDB_LEN] =
{ REQUEST_SENSE, 0, 0, 0, SCSI_SENSE_BUFFERSIZE, 0 };
struct ata_port *ap = dev->link->ap;
struct ata_taskfile tf;
DPRINTK("ATAPI request sense\n");
memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE);
/* initialize sense_buf with the error register,
* for the case where they are -not- overwritten
*/
sense_buf[0] = 0x70;
sense_buf[2] = dfl_sense_key;
/* some devices time out if garbage left in tf */
ata_tf_init(dev, &tf);
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf.command = ATA_CMD_PACKET;
/* is it pointless to prefer PIO for "safety reasons"? */
if (ap->flags & ATA_FLAG_PIO_DMA) {
tf.protocol = ATAPI_PROT_DMA;
tf.feature |= ATAPI_PKT_DMA;
} else {
tf.protocol = ATAPI_PROT_PIO;
tf.lbam = SCSI_SENSE_BUFFERSIZE;
tf.lbah = 0;
}
return ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
sense_buf, SCSI_SENSE_BUFFERSIZE, 0);
}
/**
* ata_eh_analyze_serror - analyze SError for a failed port
* @link: ATA link to analyze SError for
*
* Analyze SError if available and further determine cause of
* failure.
*
* LOCKING:
* None.
*/
static void ata_eh_analyze_serror(struct ata_link *link)
{
struct ata_eh_context *ehc = &link->eh_context;
u32 serror = ehc->i.serror;
unsigned int err_mask = 0, action = 0;
u32 hotplug_mask;
if (serror & (SERR_PERSISTENT | SERR_DATA)) {
err_mask |= AC_ERR_ATA_BUS;
action |= ATA_EH_RESET;
}
if (serror & SERR_PROTOCOL) {
err_mask |= AC_ERR_HSM;
action |= ATA_EH_RESET;
}
if (serror & SERR_INTERNAL) {
err_mask |= AC_ERR_SYSTEM;
action |= ATA_EH_RESET;
}
/* Determine whether a hotplug event has occurred. Both
* SError.N/X are considered hotplug events for enabled or
* host links. For disabled PMP links, only N bit is
* considered as X bit is left at 1 for link plugging.
*/
if (link->lpm_policy > ATA_LPM_MAX_POWER)
hotplug_mask = 0; /* hotplug doesn't work w/ LPM */
else if (!(link->flags & ATA_LFLAG_DISABLED) || ata_is_host_link(link))
hotplug_mask = SERR_PHYRDY_CHG | SERR_DEV_XCHG;
else
hotplug_mask = SERR_PHYRDY_CHG;
if (serror & hotplug_mask)
ata_ehi_hotplugged(&ehc->i);
ehc->i.err_mask |= err_mask;
ehc->i.action |= action;
}
/**
* ata_eh_analyze_ncq_error - analyze NCQ error
* @link: ATA link to analyze NCQ error for
*
* Read log page 10h, determine the offending qc and acquire
* error status TF. For NCQ device errors, all LLDDs have to do
* is setting AC_ERR_DEV in ehi->err_mask. This function takes
* care of the rest.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
void ata_eh_analyze_ncq_error(struct ata_link *link)
{
struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev = link->device;
struct ata_queued_cmd *qc;
struct ata_taskfile tf;
int tag, rc;
dev_info(link->ap->host->dev, "libata-core.c ata_eh_analyze_ncq_error(), ap->pflags = 0x%08x",ap->pflags);
/* if frozen, we can't do much */
if (ap->pflags & ATA_PFLAG_FROZEN)
return;
/* is it NCQ device error? */
if (!link->sactive || !(ehc->i.err_mask & AC_ERR_DEV))
return;
/* has LLDD analyzed already? */
for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
qc = __ata_qc_from_tag(ap, tag);
if (!(qc->flags & ATA_QCFLAG_FAILED))
continue;
if (qc->err_mask)
return;
}
/* okay, this error is ours */
memset(&tf, 0, sizeof(tf));
rc = ata_eh_read_log_10h(dev, &tag, &tf);
if (rc) {
ata_link_err(link, "failed to read log page 10h (errno=%d)\n",
rc);
return;
}
if (!(link->sactive & (1 << tag))) {
ata_link_err(link, "log page 10h reported inactive tag %d\n",
tag);
return;
}
/* we've got the perpetrator, condemn it */
qc = __ata_qc_from_tag(ap, tag);
memcpy(&qc->result_tf, &tf, sizeof(tf));
qc->result_tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_LBA | ATA_TFLAG_LBA48;
qc->err_mask |= AC_ERR_DEV | AC_ERR_NCQ;
ehc->i.err_mask &= ~AC_ERR_DEV;
}
/**
* ata_eh_analyze_tf - analyze taskfile of a failed qc
* @qc: qc to analyze
* @tf: Taskfile registers to analyze
*
* Analyze taskfile of @qc and further determine cause of
* failure. This function also requests ATAPI sense data if
* available.
*
* LOCKING:
* Kernel thread context (may sleep).
*
* RETURNS:
* Determined recovery action
*/
static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc,
const struct ata_taskfile *tf)
{
unsigned int tmp, action = 0;
u8 stat = tf->command, err = tf->feature;
if ((stat & (ATA_BUSY | ATA_DRQ | ATA_DRDY)) != ATA_DRDY) {
qc->err_mask |= AC_ERR_HSM;
return ATA_EH_RESET;
}
if (stat & (ATA_ERR | ATA_DF))
qc->err_mask |= AC_ERR_DEV;
else
return 0;
switch (qc->dev->class) {
case ATA_DEV_ATA:
case ATA_DEV_ZAC:
if (err & ATA_ICRC)
qc->err_mask |= AC_ERR_ATA_BUS;
if (err & (ATA_UNC | ATA_AMNF))
qc->err_mask |= AC_ERR_MEDIA;
if (err & ATA_IDNF)
qc->err_mask |= AC_ERR_INVALID;
break;
case ATA_DEV_ATAPI:
if (!(qc->ap->pflags & ATA_PFLAG_FROZEN)) {
tmp = atapi_eh_request_sense(qc->dev,
qc->scsicmd->sense_buffer,
qc->result_tf.feature >> 4);
if (!tmp) {
/* ATA_QCFLAG_SENSE_VALID is used to
* tell atapi_qc_complete() that sense
* data is already valid.
*
* TODO: interpret sense data and set
* appropriate err_mask.
*/
qc->flags |= ATA_QCFLAG_SENSE_VALID;
} else
qc->err_mask |= tmp;
}
}
if (qc->err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT | AC_ERR_ATA_BUS))
action |= ATA_EH_RESET;
return action;
}
static int ata_eh_categorize_error(unsigned int eflags, unsigned int err_mask,
int *xfer_ok)
{
int base = 0;
if (!(eflags & ATA_EFLAG_DUBIOUS_XFER))
*xfer_ok = 1;
if (!*xfer_ok)
base = ATA_ECAT_DUBIOUS_NONE;
if (err_mask & AC_ERR_ATA_BUS)
return base + ATA_ECAT_ATA_BUS;
if (err_mask & AC_ERR_TIMEOUT)
return base + ATA_ECAT_TOUT_HSM;
if (eflags & ATA_EFLAG_IS_IO) {
if (err_mask & AC_ERR_HSM)
return base + ATA_ECAT_TOUT_HSM;
if ((err_mask &
(AC_ERR_DEV|AC_ERR_MEDIA|AC_ERR_INVALID)) == AC_ERR_DEV)
return base + ATA_ECAT_UNK_DEV;
}
return 0;
}
struct speed_down_verdict_arg {
u64 since;
int xfer_ok;
int nr_errors[ATA_ECAT_NR];
};
static int speed_down_verdict_cb(struct ata_ering_entry *ent, void *void_arg)
{
struct speed_down_verdict_arg *arg = void_arg;
int cat;
if ((ent->eflags & ATA_EFLAG_OLD_ER) || (ent->timestamp < arg->since))
return -1;
cat = ata_eh_categorize_error(ent->eflags, ent->err_mask,
&arg->xfer_ok);
arg->nr_errors[cat]++;
return 0;
}
/**
* ata_eh_speed_down_verdict - Determine speed down verdict
* @dev: Device of interest
*
* This function examines error ring of @dev and determines
* whether NCQ needs to be turned off, transfer speed should be
* stepped down, or falling back to PIO is necessary.
*
* ECAT_ATA_BUS : ATA_BUS error for any command
*
* ECAT_TOUT_HSM : TIMEOUT for any command or HSM violation for
* IO commands
*
* ECAT_UNK_DEV : Unknown DEV error for IO commands
*
* ECAT_DUBIOUS_* : Identical to above three but occurred while
* data transfer hasn't been verified.
*
* Verdicts are
*
* NCQ_OFF : Turn off NCQ.
*
* SPEED_DOWN : Speed down transfer speed but don't fall back
* to PIO.
*
* FALLBACK_TO_PIO : Fall back to PIO.
*
* Even if multiple verdicts are returned, only one action is
* taken per error. An action triggered by non-DUBIOUS errors
* clears ering, while one triggered by DUBIOUS_* errors doesn't.
* This is to expedite speed down decisions right after device is
* initially configured.
*
* The followings are speed down rules. #1 and #2 deal with
* DUBIOUS errors.
*
* 1. If more than one DUBIOUS_ATA_BUS or DUBIOUS_TOUT_HSM errors
* occurred during last 5 mins, SPEED_DOWN and FALLBACK_TO_PIO.
*
* 2. If more than one DUBIOUS_TOUT_HSM or DUBIOUS_UNK_DEV errors
* occurred during last 5 mins, NCQ_OFF.
*
* 3. If more than 8 ATA_BUS, TOUT_HSM or UNK_DEV errors
* occurred during last 5 mins, FALLBACK_TO_PIO
*
* 4. If more than 3 TOUT_HSM or UNK_DEV errors occurred
* during last 10 mins, NCQ_OFF.
*
* 5. If more than 3 ATA_BUS or TOUT_HSM errors, or more than 6
* UNK_DEV errors occurred during last 10 mins, SPEED_DOWN.
*
* LOCKING:
* Inherited from caller.
*
* RETURNS:
* OR of ATA_EH_SPDN_* flags.
*/
static unsigned int ata_eh_speed_down_verdict(struct ata_device *dev)
{
const u64 j5mins = 5LLU * 60 * HZ, j10mins = 10LLU * 60 * HZ;
u64 j64 = get_jiffies_64();
struct speed_down_verdict_arg arg;
unsigned int verdict = 0;
/* scan past 5 mins of error history */
memset(&arg, 0, sizeof(arg));
arg.since = j64 - min(j64, j5mins);
ata_ering_map(&dev->ering, speed_down_verdict_cb, &arg);
if (arg.nr_errors[ATA_ECAT_DUBIOUS_ATA_BUS] +
arg.nr_errors[ATA_ECAT_DUBIOUS_TOUT_HSM] > 1)
verdict |= ATA_EH_SPDN_SPEED_DOWN |
ATA_EH_SPDN_FALLBACK_TO_PIO | ATA_EH_SPDN_KEEP_ERRORS;
if (arg.nr_errors[ATA_ECAT_DUBIOUS_TOUT_HSM] +
arg.nr_errors[ATA_ECAT_DUBIOUS_UNK_DEV] > 1)
verdict |= ATA_EH_SPDN_NCQ_OFF | ATA_EH_SPDN_KEEP_ERRORS;
if (arg.nr_errors[ATA_ECAT_ATA_BUS] +
arg.nr_errors[ATA_ECAT_TOUT_HSM] +
arg.nr_errors[ATA_ECAT_UNK_DEV] > 6)
verdict |= ATA_EH_SPDN_FALLBACK_TO_PIO;
/* scan past 10 mins of error history */
memset(&arg, 0, sizeof(arg));
arg.since = j64 - min(j64, j10mins);
ata_ering_map(&dev->ering, speed_down_verdict_cb, &arg);
if (arg.nr_errors[ATA_ECAT_TOUT_HSM] +
arg.nr_errors[ATA_ECAT_UNK_DEV] > 3)
verdict |= ATA_EH_SPDN_NCQ_OFF;
if (arg.nr_errors[ATA_ECAT_ATA_BUS] +
arg.nr_errors[ATA_ECAT_TOUT_HSM] > 3 ||
arg.nr_errors[ATA_ECAT_UNK_DEV] > 6)
verdict |= ATA_EH_SPDN_SPEED_DOWN;
return verdict;
}
/**
* ata_eh_speed_down - record error and speed down if necessary
* @dev: Failed device
* @eflags: mask of ATA_EFLAG_* flags
* @err_mask: err_mask of the error
*
* Record error and examine error history to determine whether
* adjusting transmission speed is necessary. It also sets
* transmission limits appropriately if such adjustment is
* necessary.
*
* LOCKING:
* Kernel thread context (may sleep).
*
* RETURNS:
* Determined recovery action.
*/
static unsigned int ata_eh_speed_down(struct ata_device *dev,
unsigned int eflags, unsigned int err_mask)
{
struct ata_link *link = ata_dev_phys_link(dev);
int xfer_ok = 0;
unsigned int verdict;
unsigned int action = 0;
/* don't bother if Cat-0 error */
if (ata_eh_categorize_error(eflags, err_mask, &xfer_ok) == 0)
return 0;
/* record error and determine whether speed down is necessary */
ata_ering_record(&dev->ering, eflags, err_mask);
verdict = ata_eh_speed_down_verdict(dev);
/* turn off NCQ? */
if ((verdict & ATA_EH_SPDN_NCQ_OFF) &&
(dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ |
ATA_DFLAG_NCQ_OFF)) == ATA_DFLAG_NCQ) {
dev->flags |= ATA_DFLAG_NCQ_OFF;
ata_dev_warn(dev, "NCQ disabled due to excessive errors\n");
goto done;
}
/* speed down? */
if (verdict & ATA_EH_SPDN_SPEED_DOWN) {
/* speed down SATA link speed if possible */
if (sata_down_spd_limit(link, 0) == 0) {
action |= ATA_EH_RESET;
goto done;
}
/* lower transfer mode */
if (dev->spdn_cnt < 2) {
static const int dma_dnxfer_sel[] =
{ ATA_DNXFER_DMA, ATA_DNXFER_40C };
static const int pio_dnxfer_sel[] =
{ ATA_DNXFER_PIO, ATA_DNXFER_FORCE_PIO0 };
int sel;
if (dev->xfer_shift != ATA_SHIFT_PIO)
sel = dma_dnxfer_sel[dev->spdn_cnt];
else
sel = pio_dnxfer_sel[dev->spdn_cnt];
dev->spdn_cnt++;
if (ata_down_xfermask_limit(dev, sel) == 0) {
action |= ATA_EH_RESET;
goto done;
}
}
}
/* Fall back to PIO? Slowing down to PIO is meaningless for
* SATA ATA devices. Consider it only for PATA and SATAPI.
*/
if ((verdict & ATA_EH_SPDN_FALLBACK_TO_PIO) && (dev->spdn_cnt >= 2) &&
(link->ap->cbl != ATA_CBL_SATA || dev->class == ATA_DEV_ATAPI) &&
(dev->xfer_shift != ATA_SHIFT_PIO)) {
if (ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO) == 0) {
dev->spdn_cnt = 0;
action |= ATA_EH_RESET;
goto done;
}
}
return 0;
done:
/* device has been slowed down, blow error history */
if (!(verdict & ATA_EH_SPDN_KEEP_ERRORS))
ata_ering_clear(&dev->ering);
return action;
}
/**
* ata_eh_worth_retry - analyze error and decide whether to retry
* @qc: qc to possibly retry
*
* Look at the cause of the error and decide if a retry
* might be useful or not. We don't want to retry media errors
* because the drive itself has probably already taken 10-30 seconds
* doing its own internal retries before reporting the failure.
*/
static inline int ata_eh_worth_retry(struct ata_queued_cmd *qc)
{
if (qc->err_mask & AC_ERR_MEDIA)
return 0; /* don't retry media errors */
if (qc->flags & ATA_QCFLAG_IO)
return 1; /* otherwise retry anything from fs stack */
if (qc->err_mask & AC_ERR_INVALID)
return 0; /* don't retry these */
return qc->err_mask != AC_ERR_DEV; /* retry if not dev error */
}
/**
* ata_eh_link_autopsy - analyze error and determine recovery action
* @link: host link to perform autopsy on
*
* Analyze why @link failed and determine which recovery actions
* are needed. This function also sets more detailed AC_ERR_*
* values and fills sense data for ATAPI CHECK SENSE.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
static void ata_eh_link_autopsy(struct ata_link *link)
{
struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev;
unsigned int all_err_mask = 0, eflags = 0;
int tag;
u32 serror;
int rc;
dev_info(link->ap->host->dev, "libata-core.c ata_eh_link_autopsy(), ehc->i.flags = 0x%08x, ehc->i.probe_mask = 0x%08x, ehc->i.action = 0x%08x, ehc->i.err_mask = 0x%08x,",
ehc->i.flags,ehc->i.probe_mask, ehc->i.action, ehc->i.err_mask);
DPRINTK("ENTER\n");
// Elphel: Do always
// if (ehc->i.flags & ATA_EHI_NO_AUTOPSY)
// return;
/* obtain and analyze SError */
rc = sata_scr_read(link, SCR_ERROR, &serror);
if (rc == 0) {
ehc->i.serror |= serror;
ata_eh_analyze_serror(link);
} else if (rc != -EOPNOTSUPP) {
/* SError read failed, force reset and probing */
ehc->i.probe_mask |= ATA_ALL_DEVICES;
ehc->i.action |= ATA_EH_RESET;
ehc->i.err_mask |= AC_ERR_OTHER;
}
dev_info(link->ap->host->dev, "libata-core.c ata_eh_link_autopsy(), rc = 0x%08x, ehc->i.probe_mask = 0x%08x, ehc->i.action = 0x%08x, ehc->i.err_mask = 0x%08x,",
rc, ehc->i.probe_mask, ehc->i.action, ehc->i.err_mask);
/* analyze NCQ failure */
ata_eh_analyze_ncq_error(link);
/* any real error trumps AC_ERR_OTHER */
if (ehc->i.err_mask & ~AC_ERR_OTHER)
ehc->i.err_mask &= ~AC_ERR_OTHER;
all_err_mask |= ehc->i.err_mask;
dev_info(link->ap->host->dev, "libata-core.c ata_eh_link_autopsy(), ehc->i.err_mask = 0x%08x, all_err_mask = 0x%08x",
ehc->i.err_mask,all_err_mask);
for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag);
if (!(qc->flags & ATA_QCFLAG_FAILED) ||
ata_dev_phys_link(qc->dev) != link)
continue;
/* inherit upper level err_mask */
qc->err_mask |= ehc->i.err_mask;
/* analyze TF */
ehc->i.action |= ata_eh_analyze_tf(qc, &qc->result_tf);
/* DEV errors are probably spurious in case of ATA_BUS error */
if (qc->err_mask & AC_ERR_ATA_BUS)
qc->err_mask &= ~(AC_ERR_DEV | AC_ERR_MEDIA |
AC_ERR_INVALID);
/* any real error trumps unknown error */
if (qc->err_mask & ~AC_ERR_OTHER)
qc->err_mask &= ~AC_ERR_OTHER;
/* SENSE_VALID trumps dev/unknown error and revalidation */
if (qc->flags & ATA_QCFLAG_SENSE_VALID)
qc->err_mask &= ~(AC_ERR_DEV | AC_ERR_OTHER);
/* determine whether the command is worth retrying */
if (ata_eh_worth_retry(qc))
qc->flags |= ATA_QCFLAG_RETRY;
/* accumulate error info */
ehc->i.dev = qc->dev;
all_err_mask |= qc->err_mask;
if (qc->flags & ATA_QCFLAG_IO)
eflags |= ATA_EFLAG_IS_IO;
}
dev_info(link->ap->host->dev, "libata-core.c ata_eh_link_autopsy(), all_err_mask = 0x%08x", all_err_mask);
/* enforce default EH actions */
if (ap->pflags & ATA_PFLAG_FROZEN ||
all_err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT))
ehc->i.action |= ATA_EH_RESET;
else if (((eflags & ATA_EFLAG_IS_IO) && all_err_mask) ||
(!(eflags & ATA_EFLAG_IS_IO) && (all_err_mask & ~AC_ERR_DEV)))
ehc->i.action |= ATA_EH_REVALIDATE;
/* If we have offending qcs and the associated failed device,
* perform per-dev EH action only on the offending device.
*/
if (ehc->i.dev) {
ehc->i.dev_action[ehc->i.dev->devno] |=
ehc->i.action & ATA_EH_PERDEV_MASK;
ehc->i.action &= ~ATA_EH_PERDEV_MASK;
}
/* propagate timeout to host link */
if ((all_err_mask & AC_ERR_TIMEOUT) && !ata_is_host_link(link))
ap->link.eh_context.i.err_mask |= AC_ERR_TIMEOUT;
/* record error and consider speeding down */
dev = ehc->i.dev;
if (!dev && ((ata_link_max_devices(link) == 1 &&
ata_dev_enabled(link->device))))
dev = link->device;
if (dev) {
if (dev->flags & ATA_DFLAG_DUBIOUS_XFER)
eflags |= ATA_EFLAG_DUBIOUS_XFER;
ehc->i.action |= ata_eh_speed_down(dev, eflags, all_err_mask);
}
dev_info(link->ap->host->dev, "libata-core.c ata_eh_link_autopsy(), conclusion what to do: ehc->i.action = 0x%08x", ehc->i.action);
DPRINTK("EXIT\n");
}
/**
* ata_eh_autopsy - analyze error and determine recovery action
* @ap: host port to perform autopsy on
*
* Analyze all links of @ap and determine why they failed and
* which recovery actions are needed.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
void ata_eh_autopsy(struct ata_port *ap)
{
struct ata_link *link;
dev_info(ap->host->dev, "libata-core.c ata_eh_autopsy()");
ata_for_each_link(link, ap, EDGE)
ata_eh_link_autopsy(link);
/* Handle the frigging slave link. Autopsy is done similarly
* but actions and flags are transferred over to the master
* link and handled from there.
*/
if (ap->slave_link) {
struct ata_eh_context *mehc = &ap->link.eh_context;
struct ata_eh_context *sehc = &ap->slave_link->eh_context;
/* transfer control flags from master to slave */
sehc->i.flags |= mehc->i.flags & ATA_EHI_TO_SLAVE_MASK;
/* perform autopsy on the slave link */
ata_eh_link_autopsy(ap->slave_link);
/* transfer actions from slave to master and clear slave */
ata_eh_about_to_do(ap->slave_link, NULL, ATA_EH_ALL_ACTIONS);
mehc->i.action |= sehc->i.action;
mehc->i.dev_action[1] |= sehc->i.dev_action[1];
mehc->i.flags |= sehc->i.flags;
ata_eh_done(ap->slave_link, NULL, ATA_EH_ALL_ACTIONS);
}
/* Autopsy of fanout ports can affect host link autopsy.
* Perform host link autopsy last.
*/
if (sata_pmp_attached(ap))
ata_eh_link_autopsy(&ap->link);
}
/**
* ata_get_cmd_descript - get description for ATA command
* @command: ATA command code to get description for
*
* Return a textual description of the given command, or NULL if the
* command is not known.
*
* LOCKING:
* None
*/
const char *ata_get_cmd_descript(u8 command)
{
#ifdef CONFIG_ATA_VERBOSE_ERROR
static const struct
{
u8 command;
const char *text;
} cmd_descr[] = {
{ ATA_CMD_DEV_RESET, "DEVICE RESET" },
{ ATA_CMD_CHK_POWER, "CHECK POWER MODE" },
{ ATA_CMD_STANDBY, "STANDBY" },
{ ATA_CMD_IDLE, "IDLE" },
{ ATA_CMD_EDD, "EXECUTE DEVICE DIAGNOSTIC" },
{ ATA_CMD_DOWNLOAD_MICRO, "DOWNLOAD MICROCODE" },
{ ATA_CMD_DOWNLOAD_MICRO_DMA, "DOWNLOAD MICROCODE DMA" },
{ ATA_CMD_NOP, "NOP" },
{ ATA_CMD_FLUSH, "FLUSH CACHE" },
{ ATA_CMD_FLUSH_EXT, "FLUSH CACHE EXT" },
{ ATA_CMD_ID_ATA, "IDENTIFY DEVICE" },
{ ATA_CMD_ID_ATAPI, "IDENTIFY PACKET DEVICE" },
{ ATA_CMD_SERVICE, "SERVICE" },
{ ATA_CMD_READ, "READ DMA" },
{ ATA_CMD_READ_EXT, "READ DMA EXT" },
{ ATA_CMD_READ_QUEUED, "READ DMA QUEUED" },
{ ATA_CMD_READ_STREAM_EXT, "READ STREAM EXT" },
{ ATA_CMD_READ_STREAM_DMA_EXT, "READ STREAM DMA EXT" },
{ ATA_CMD_WRITE, "WRITE DMA" },
{ ATA_CMD_WRITE_EXT, "WRITE DMA EXT" },
{ ATA_CMD_WRITE_QUEUED, "WRITE DMA QUEUED EXT" },
{ ATA_CMD_WRITE_STREAM_EXT, "WRITE STREAM EXT" },
{ ATA_CMD_WRITE_STREAM_DMA_EXT, "WRITE STREAM DMA EXT" },
{ ATA_CMD_WRITE_FUA_EXT, "WRITE DMA FUA EXT" },
{ ATA_CMD_WRITE_QUEUED_FUA_EXT, "WRITE DMA QUEUED FUA EXT" },
{ ATA_CMD_FPDMA_READ, "READ FPDMA QUEUED" },
{ ATA_CMD_FPDMA_WRITE, "WRITE FPDMA QUEUED" },
{ ATA_CMD_FPDMA_SEND, "SEND FPDMA QUEUED" },
{ ATA_CMD_FPDMA_RECV, "RECEIVE FPDMA QUEUED" },
{ ATA_CMD_PIO_READ, "READ SECTOR(S)" },
{ ATA_CMD_PIO_READ_EXT, "READ SECTOR(S) EXT" },
{ ATA_CMD_PIO_WRITE, "WRITE SECTOR(S)" },
{ ATA_CMD_PIO_WRITE_EXT, "WRITE SECTOR(S) EXT" },
{ ATA_CMD_READ_MULTI, "READ MULTIPLE" },
{ ATA_CMD_READ_MULTI_EXT, "READ MULTIPLE EXT" },
{ ATA_CMD_WRITE_MULTI, "WRITE MULTIPLE" },
{ ATA_CMD_WRITE_MULTI_EXT, "WRITE MULTIPLE EXT" },
{ ATA_CMD_WRITE_MULTI_FUA_EXT, "WRITE MULTIPLE FUA EXT" },
{ ATA_CMD_SET_FEATURES, "SET FEATURES" },
{ ATA_CMD_SET_MULTI, "SET MULTIPLE MODE" },
{ ATA_CMD_VERIFY, "READ VERIFY SECTOR(S)" },
{ ATA_CMD_VERIFY_EXT, "READ VERIFY SECTOR(S) EXT" },
{ ATA_CMD_WRITE_UNCORR_EXT, "WRITE UNCORRECTABLE EXT" },
{ ATA_CMD_STANDBYNOW1, "STANDBY IMMEDIATE" },
{ ATA_CMD_IDLEIMMEDIATE, "IDLE IMMEDIATE" },
{ ATA_CMD_SLEEP, "SLEEP" },
{ ATA_CMD_INIT_DEV_PARAMS, "INITIALIZE DEVICE PARAMETERS" },
{ ATA_CMD_READ_NATIVE_MAX, "READ NATIVE MAX ADDRESS" },
{ ATA_CMD_READ_NATIVE_MAX_EXT, "READ NATIVE MAX ADDRESS EXT" },
{ ATA_CMD_SET_MAX, "SET MAX ADDRESS" },
{ ATA_CMD_SET_MAX_EXT, "SET MAX ADDRESS EXT" },
{ ATA_CMD_READ_LOG_EXT, "READ LOG EXT" },
{ ATA_CMD_WRITE_LOG_EXT, "WRITE LOG EXT" },
{ ATA_CMD_READ_LOG_DMA_EXT, "READ LOG DMA EXT" },
{ ATA_CMD_WRITE_LOG_DMA_EXT, "WRITE LOG DMA EXT" },
{ ATA_CMD_TRUSTED_NONDATA, "TRUSTED NON-DATA" },
{ ATA_CMD_TRUSTED_RCV, "TRUSTED RECEIVE" },
{ ATA_CMD_TRUSTED_RCV_DMA, "TRUSTED RECEIVE DMA" },
{ ATA_CMD_TRUSTED_SND, "TRUSTED SEND" },
{ ATA_CMD_TRUSTED_SND_DMA, "TRUSTED SEND DMA" },
{ ATA_CMD_PMP_READ, "READ BUFFER" },
{ ATA_CMD_PMP_READ_DMA, "READ BUFFER DMA" },
{ ATA_CMD_PMP_WRITE, "WRITE BUFFER" },
{ ATA_CMD_PMP_WRITE_DMA, "WRITE BUFFER DMA" },
{ ATA_CMD_CONF_OVERLAY, "DEVICE CONFIGURATION OVERLAY" },
{ ATA_CMD_SEC_SET_PASS, "SECURITY SET PASSWORD" },
{ ATA_CMD_SEC_UNLOCK, "SECURITY UNLOCK" },
{ ATA_CMD_SEC_ERASE_PREP, "SECURITY ERASE PREPARE" },
{ ATA_CMD_SEC_ERASE_UNIT, "SECURITY ERASE UNIT" },
{ ATA_CMD_SEC_FREEZE_LOCK, "SECURITY FREEZE LOCK" },
{ ATA_CMD_SEC_DISABLE_PASS, "SECURITY DISABLE PASSWORD" },
{ ATA_CMD_CONFIG_STREAM, "CONFIGURE STREAM" },
{ ATA_CMD_SMART, "SMART" },
{ ATA_CMD_MEDIA_LOCK, "DOOR LOCK" },
{ ATA_CMD_MEDIA_UNLOCK, "DOOR UNLOCK" },
{ ATA_CMD_DSM, "DATA SET MANAGEMENT" },
{ ATA_CMD_CHK_MED_CRD_TYP, "CHECK MEDIA CARD TYPE" },
{ ATA_CMD_CFA_REQ_EXT_ERR, "CFA REQUEST EXTENDED ERROR" },
{ ATA_CMD_CFA_WRITE_NE, "CFA WRITE SECTORS WITHOUT ERASE" },
{ ATA_CMD_CFA_TRANS_SECT, "CFA TRANSLATE SECTOR" },
{ ATA_CMD_CFA_ERASE, "CFA ERASE SECTORS" },
{ ATA_CMD_CFA_WRITE_MULT_NE, "CFA WRITE MULTIPLE WITHOUT ERASE" },
{ ATA_CMD_REQ_SENSE_DATA, "REQUEST SENSE DATA EXT" },
{ ATA_CMD_SANITIZE_DEVICE, "SANITIZE DEVICE" },
{ ATA_CMD_READ_LONG, "READ LONG (with retries)" },
{ ATA_CMD_READ_LONG_ONCE, "READ LONG (without retries)" },
{ ATA_CMD_WRITE_LONG, "WRITE LONG (with retries)" },
{ ATA_CMD_WRITE_LONG_ONCE, "WRITE LONG (without retries)" },
{ ATA_CMD_RESTORE, "RECALIBRATE" },
{ 0, NULL } /* terminate list */
};
unsigned int i;
for (i = 0; cmd_descr[i].text; i++)
if (cmd_descr[i].command == command)
return cmd_descr[i].text;
#endif
return NULL;
}
EXPORT_SYMBOL_GPL(ata_get_cmd_descript);
/**
* ata_eh_link_report - report error handling to user
* @link: ATA link EH is going on
*
* Report EH to user.
*
* LOCKING:
* None.
*/
static void ata_eh_link_report(struct ata_link *link)
{
struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
const char *frozen, *desc;
char tries_buf[6] = "";
int tag, nr_failed = 0;
if (ehc->i.flags & ATA_EHI_QUIET)
return;
desc = NULL;
if (ehc->i.desc[0] != '\0')
desc = ehc->i.desc;
for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag);
if (!(qc->flags & ATA_QCFLAG_FAILED) ||
ata_dev_phys_link(qc->dev) != link ||
((qc->flags & ATA_QCFLAG_QUIET) &&
qc->err_mask == AC_ERR_DEV))
continue;
if (qc->flags & ATA_QCFLAG_SENSE_VALID && !qc->err_mask)
continue;
nr_failed++;
}
if (!nr_failed && !ehc->i.err_mask)
return;
frozen = "";
if (ap->pflags & ATA_PFLAG_FROZEN)
frozen = " frozen";
if (ap->eh_tries < ATA_EH_MAX_TRIES)
snprintf(tries_buf, sizeof(tries_buf), " t%d",
ap->eh_tries);
if (ehc->i.dev) {
ata_dev_err(ehc->i.dev, "exception Emask 0x%x "
"SAct 0x%x SErr 0x%x action 0x%x%s%s\n",
ehc->i.err_mask, link->sactive, ehc->i.serror,
ehc->i.action, frozen, tries_buf);
if (desc)
ata_dev_err(ehc->i.dev, "%s\n", desc);
} else {
ata_link_err(link, "exception Emask 0x%x "
"SAct 0x%x SErr 0x%x action 0x%x%s%s\n",
ehc->i.err_mask, link->sactive, ehc->i.serror,
ehc->i.action, frozen, tries_buf);
if (desc)
ata_link_err(link, "%s\n", desc);
}
#ifdef CONFIG_ATA_VERBOSE_ERROR
if (ehc->i.serror)
ata_link_err(link,
"SError: { %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s}\n",
ehc->i.serror & SERR_DATA_RECOVERED ? "RecovData " : "",
ehc->i.serror & SERR_COMM_RECOVERED ? "RecovComm " : "",
ehc->i.serror & SERR_DATA ? "UnrecovData " : "",
ehc->i.serror & SERR_PERSISTENT ? "Persist " : "",
ehc->i.serror & SERR_PROTOCOL ? "Proto " : "",
ehc->i.serror & SERR_INTERNAL ? "HostInt " : "",
ehc->i.serror & SERR_PHYRDY_CHG ? "PHYRdyChg " : "",
ehc->i.serror & SERR_PHY_INT_ERR ? "PHYInt " : "",
ehc->i.serror & SERR_COMM_WAKE ? "CommWake " : "",
ehc->i.serror & SERR_10B_8B_ERR ? "10B8B " : "",
ehc->i.serror & SERR_DISPARITY ? "Dispar " : "",
ehc->i.serror & SERR_CRC ? "BadCRC " : "",
ehc->i.serror & SERR_HANDSHAKE ? "Handshk " : "",
ehc->i.serror & SERR_LINK_SEQ_ERR ? "LinkSeq " : "",
ehc->i.serror & SERR_TRANS_ST_ERROR ? "TrStaTrns " : "",
ehc->i.serror & SERR_UNRECOG_FIS ? "UnrecFIS " : "",
ehc->i.serror & SERR_DEV_XCHG ? "DevExch " : "");
#endif
for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag);
struct ata_taskfile *cmd = &qc->tf, *res = &qc->result_tf;
char data_buf[20] = "";
char cdb_buf[70] = "";
if (!(qc->flags & ATA_QCFLAG_FAILED) ||
ata_dev_phys_link(qc->dev) != link || !qc->err_mask)
continue;
if (qc->dma_dir != DMA_NONE) {
static const char *dma_str[] = {
[DMA_BIDIRECTIONAL] = "bidi",
[DMA_TO_DEVICE] = "out",
[DMA_FROM_DEVICE] = "in",
};
static const char *prot_str[] = {
[ATA_PROT_PIO] = "pio",
[ATA_PROT_DMA] = "dma",
[ATA_PROT_NCQ] = "ncq",
[ATAPI_PROT_PIO] = "pio",
[ATAPI_PROT_DMA] = "dma",
};
snprintf(data_buf, sizeof(data_buf), " %s %u %s",
prot_str[qc->tf.protocol], qc->nbytes,
dma_str[qc->dma_dir]);
}
if (ata_is_atapi(qc->tf.protocol)) {
const u8 *cdb = qc->cdb;
size_t cdb_len = qc->dev->cdb_len;
if (qc->scsicmd) {
cdb = qc->scsicmd->cmnd;
cdb_len = qc->scsicmd->cmd_len;
}
__scsi_format_command(cdb_buf, sizeof(cdb_buf),
cdb, cdb_len);
} else {
const char *descr = ata_get_cmd_descript(cmd->command);
if (descr)
ata_dev_err(qc->dev, "failed command: %s\n",
descr);
}
ata_dev_err(qc->dev,
"cmd %02x/%02x:%02x:%02x:%02x:%02x/%02x:%02x:%02x:%02x:%02x/%02x "
"tag %d%s\n %s"
"res %02x/%02x:%02x:%02x:%02x:%02x/%02x:%02x:%02x:%02x:%02x/%02x "
"Emask 0x%x (%s)%s\n",
cmd->command, cmd->feature, cmd->nsect,
cmd->lbal, cmd->lbam, cmd->lbah,
cmd->hob_feature, cmd->hob_nsect,
cmd->hob_lbal, cmd->hob_lbam, cmd->hob_lbah,
cmd->device, qc->tag, data_buf, cdb_buf,
res->command, res->feature, res->nsect,
res->lbal, res->lbam, res->lbah,
res->hob_feature, res->hob_nsect,
res->hob_lbal, res->hob_lbam, res->hob_lbah,
res->device, qc->err_mask, ata_err_string(qc->err_mask),
qc->err_mask & AC_ERR_NCQ ? " " : "");
// elphel test: crash driver here to stop its execution and preserve memory state
//u32 *tmp_str = NULL;
//ata_dev_err(qc->dev, "test: %u", *tmp_str);
#ifdef CONFIG_ATA_VERBOSE_ERROR
if (res->command & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ |
ATA_ERR)) {
if (res->command & ATA_BUSY)
ata_dev_err(qc->dev, "status: { Busy }\n");
else
ata_dev_err(qc->dev, "status: { %s%s%s%s}\n",
res->command & ATA_DRDY ? "DRDY " : "",
res->command & ATA_DF ? "DF " : "",
res->command & ATA_DRQ ? "DRQ " : "",
res->command & ATA_ERR ? "ERR " : "");
}
if (cmd->command != ATA_CMD_PACKET &&
(res->feature & (ATA_ICRC | ATA_UNC | ATA_AMNF |
ATA_IDNF | ATA_ABORTED)))
ata_dev_err(qc->dev, "error: { %s%s%s%s%s}\n",
res->feature & ATA_ICRC ? "ICRC " : "",
res->feature & ATA_UNC ? "UNC " : "",
res->feature & ATA_AMNF ? "AMNF " : "",
res->feature & ATA_IDNF ? "IDNF " : "",
res->feature & ATA_ABORTED ? "ABRT " : "");
#endif
}
}
/**
* ata_eh_report - report error handling to user
* @ap: ATA port to report EH about
*
* Report EH to user.
*
* LOCKING:
* None.
*/
void ata_eh_report(struct ata_port *ap)
{
struct ata_link *link;
ata_for_each_link(link, ap, HOST_FIRST)
ata_eh_link_report(link);
}
static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset,
unsigned int *classes, unsigned long deadline,
bool clear_classes)
{
struct ata_device *dev;
if (clear_classes)
ata_for_each_dev(dev, link, ALL)
classes[dev->devno] = ATA_DEV_UNKNOWN;
return reset(link, classes, deadline);
}
static int ata_eh_followup_srst_needed(struct ata_link *link, int rc)
{
if ((link->flags & ATA_LFLAG_NO_SRST) || ata_link_offline(link))
return 0;
if (rc == -EAGAIN)
return 1;
if (sata_pmp_supported(link->ap) && ata_is_host_link(link))
return 1;
return 0;
}
int ata_eh_reset(struct ata_link *link, int classify,
ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
{
struct ata_port *ap = link->ap;
struct ata_link *slave = ap->slave_link;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_eh_context *sehc = slave ? &slave->eh_context : NULL;
unsigned int *classes = ehc->classes;
unsigned int lflags = link->flags;
int verbose = !(ehc->i.flags & ATA_EHI_QUIET);
int max_tries = 0, try = 0;
struct ata_link *failed_link;
struct ata_device *dev;
unsigned long deadline, now;
ata_reset_fn_t reset;
unsigned long flags;
u32 sstatus;
int nr_unknown, rc;
dev_info(ap->host->dev, "ata_eh_reset(), link->flags = 0x%08x\n",link->flags);
/*
* Prepare to reset
*/
while (ata_eh_reset_timeouts[max_tries] != ULONG_MAX)
max_tries++;
if (link->flags & ATA_LFLAG_RST_ONCE)
max_tries = 1;
if (link->flags & ATA_LFLAG_NO_HRST)
hardreset = NULL;
if (link->flags & ATA_LFLAG_NO_SRST)
softreset = NULL;
/* make sure each reset attempt is at least COOL_DOWN apart */
if (ehc->i.flags & ATA_EHI_DID_RESET) {
now = jiffies;
WARN_ON(time_after(ehc->last_reset, now));
deadline = ata_deadline(ehc->last_reset,
ATA_EH_RESET_COOL_DOWN);
if (time_before(now, deadline))
schedule_timeout_uninterruptible(deadline - now);
}
spin_lock_irqsave(ap->lock, flags);
ap->pflags |= ATA_PFLAG_RESETTING;
spin_unlock_irqrestore(ap->lock, flags);
ata_eh_about_to_do(link, NULL, ATA_EH_RESET);
ata_for_each_dev(dev, link, ALL) {
/* If we issue an SRST then an ATA drive (not ATAPI)
* may change configuration and be in PIO0 timing. If
* we do a hard reset (or are coming from power on)
* this is true for ATA or ATAPI. Until we've set a
* suitable controller mode we should not touch the
* bus as we may be talking too fast.
*/
dev->pio_mode = XFER_PIO_0;
dev->dma_mode = 0xff;
/* If the controller has a pio mode setup function
* then use it to set the chipset to rights. Don't
* touch the DMA setup as that will be dealt with when
* configuring devices.
*/
if (ap->ops->set_piomode)
ap->ops->set_piomode(ap, dev);
}
/* prefer hardreset */
reset = NULL;
ehc->i.action &= ~ATA_EH_RESET;
if (hardreset) {
reset = hardreset;
ehc->i.action |= ATA_EH_HARDRESET;
} else if (softreset) {
reset = softreset;
ehc->i.action |= ATA_EH_SOFTRESET;
}
if (prereset) {
unsigned long deadline = ata_deadline(jiffies,
ATA_EH_PRERESET_TIMEOUT);
if (slave) {
sehc->i.action &= ~ATA_EH_RESET;
sehc->i.action |= ehc->i.action;
}
rc = prereset(link, deadline);
/* If present, do prereset on slave link too. Reset
* is skipped iff both master and slave links report
* -ENOENT or clear ATA_EH_RESET.
*/
if (slave && (rc == 0 || rc == -ENOENT)) {
int tmp;
tmp = prereset(slave, deadline);
if (tmp != -ENOENT)
rc = tmp;
ehc->i.action |= sehc->i.action;
}
if (rc) {
if (rc == -ENOENT) {
ata_link_dbg(link, "port disabled--ignoring\n");
ehc->i.action &= ~ATA_EH_RESET;
ata_for_each_dev(dev, link, ALL)
classes[dev->devno] = ATA_DEV_NONE;
rc = 0;
} else
ata_link_err(link,
"prereset failed (errno=%d)\n",
rc);
goto out;
}
/* prereset() might have cleared ATA_EH_RESET. If so,
* bang classes, thaw and return.
*/
if (reset && !(ehc->i.action & ATA_EH_RESET)) {
ata_for_each_dev(dev, link, ALL)
classes[dev->devno] = ATA_DEV_NONE;
if ((ap->pflags & ATA_PFLAG_FROZEN) &&
ata_is_host_link(link))
ata_eh_thaw_port(ap);
rc = 0;
goto out;
}
}
retry:
/*
* Perform reset
*/
if (ata_is_host_link(link))
ata_eh_freeze_port(ap);
deadline = ata_deadline(jiffies, ata_eh_reset_timeouts[try++]);
if (reset) {
if (verbose)
ata_link_info(link, "%s resetting link\n",
reset == softreset ? "soft" : "hard");
/* mark that this EH session started with reset */
ehc->last_reset = jiffies;
if (reset == hardreset)
ehc->i.flags |= ATA_EHI_DID_HARDRESET;
else
ehc->i.flags |= ATA_EHI_DID_SOFTRESET;
rc = ata_do_reset(link, reset, classes, deadline, true);
if (rc && rc != -EAGAIN) {
failed_link = link;
goto fail;
}
/* hardreset slave link if existent */
if (slave && reset == hardreset) {
int tmp;
if (verbose)
ata_link_info(slave, "hard resetting link\n");
ata_eh_about_to_do(slave, NULL, ATA_EH_RESET);
tmp = ata_do_reset(slave, reset, classes, deadline,
false);
switch (tmp) {
case -EAGAIN:
rc = -EAGAIN;
case 0:
break;
default:
failed_link = slave;
rc = tmp;
goto fail;
}
}
/* perform follow-up SRST if necessary */
if (reset == hardreset &&
ata_eh_followup_srst_needed(link, rc)) {
reset = softreset;
if (!reset) {
ata_link_err(link,
"follow-up softreset required but no softreset available\n");
failed_link = link;
rc = -EINVAL;
goto fail;
}
ata_eh_about_to_do(link, NULL, ATA_EH_RESET);
rc = ata_do_reset(link, reset, classes, deadline, true);
if (rc) {
failed_link = link;
goto fail;
}
}
} else {
if (verbose)
ata_link_info(link,
"no reset method available, skipping reset\n");
if (!(lflags & ATA_LFLAG_ASSUME_CLASS))
lflags |= ATA_LFLAG_ASSUME_ATA;
}
/*
* Post-reset processing
*/
ata_for_each_dev(dev, link, ALL) {
/* After the reset, the device state is PIO 0 and the
* controller state is undefined. Reset also wakes up
* drives from sleeping mode.
*/
dev->pio_mode = XFER_PIO_0;
dev->flags &= ~ATA_DFLAG_SLEEPING;
if (ata_phys_link_offline(ata_dev_phys_link(dev)))
continue;
/* apply class override */
if (lflags & ATA_LFLAG_ASSUME_ATA)
classes[dev->devno] = ATA_DEV_ATA;
else if (lflags & ATA_LFLAG_ASSUME_SEMB)
classes[dev->devno] = ATA_DEV_SEMB_UNSUP;
}
/* record current link speed */
if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0)
link->sata_spd = (sstatus >> 4) & 0xf;
if (slave && sata_scr_read(slave, SCR_STATUS, &sstatus) == 0)
slave->sata_spd = (sstatus >> 4) & 0xf;
/* thaw the port */
if (ata_is_host_link(link))
ata_eh_thaw_port(ap);
/* postreset() should clear hardware SError. Although SError
* is cleared during link resume, clearing SError here is
* necessary as some PHYs raise hotplug events after SRST.
* This introduces race condition where hotplug occurs between
* reset and here. This race is mediated by cross checking
* link onlineness and classification result later.
*/
if (postreset) {
postreset(link, classes);
if (slave)
postreset(slave, classes);
}
/*
* Some controllers can't be frozen very well and may set spurious
* error conditions during reset. Clear accumulated error
* information and re-thaw the port if frozen. As reset is the
* final recovery action and we cross check link onlineness against
* device classification later, no hotplug event is lost by this.
*/
spin_lock_irqsave(link->ap->lock, flags);
memset(&link->eh_info, 0, sizeof(link->eh_info));
if (slave)
memset(&slave->eh_info, 0, sizeof(link->eh_info));
ap->pflags &= ~ATA_PFLAG_EH_PENDING;
spin_unlock_irqrestore(link->ap->lock, flags);
if (ap->pflags & ATA_PFLAG_FROZEN)
ata_eh_thaw_port(ap);
/*
* Make sure onlineness and classification result correspond.
* Hotplug could have happened during reset and some
* controllers fail to wait while a drive is spinning up after
* being hotplugged causing misdetection. By cross checking
* link on/offlineness and classification result, those
* conditions can be reliably detected and retried.
*/
nr_unknown = 0;
ata_for_each_dev(dev, link, ALL) {
if (ata_phys_link_online(ata_dev_phys_link(dev))) {
if (classes[dev->devno] == ATA_DEV_UNKNOWN) {
ata_dev_dbg(dev, "link online but device misclassified\n");
classes[dev->devno] = ATA_DEV_NONE;
nr_unknown++;
}
} else if (ata_phys_link_offline(ata_dev_phys_link(dev))) {
if (ata_class_enabled(classes[dev->devno]))
ata_dev_dbg(dev,
"link offline, clearing class %d to NONE\n",
classes[dev->devno]);
classes[dev->devno] = ATA_DEV_NONE;
} else if (classes[dev->devno] == ATA_DEV_UNKNOWN) {
ata_dev_dbg(dev,
"link status unknown, clearing UNKNOWN to NONE\n");
classes[dev->devno] = ATA_DEV_NONE;
}
}
if (classify && nr_unknown) {
if (try < max_tries) {
ata_link_warn(link,
"link online but %d devices misclassified, retrying\n",
nr_unknown);
failed_link = link;
rc = -EAGAIN;
goto fail;
}
ata_link_warn(link,
"link online but %d devices misclassified, "
"device detection might fail\n", nr_unknown);
}
/* reset successful, schedule revalidation */
ata_eh_done(link, NULL, ATA_EH_RESET);
if (slave)
ata_eh_done(slave, NULL, ATA_EH_RESET);
ehc->last_reset = jiffies; /* update to completion time */
ehc->i.action |= ATA_EH_REVALIDATE;
link->lpm_policy = ATA_LPM_UNKNOWN; /* reset LPM state */
rc = 0;
out:
/* clear hotplug flag */
ehc->i.flags &= ~ATA_EHI_HOTPLUGGED;
if (slave)
sehc->i.flags &= ~ATA_EHI_HOTPLUGGED;
spin_lock_irqsave(ap->lock, flags);
ap->pflags &= ~ATA_PFLAG_RESETTING;
spin_unlock_irqrestore(ap->lock, flags);
return rc;
fail:
/* if SCR isn't accessible on a fan-out port, PMP needs to be reset */
if (!ata_is_host_link(link) &&
sata_scr_read(link, SCR_STATUS, &sstatus))
rc = -ERESTART;
if (try >= max_tries) {
/*
* Thaw host port even if reset failed, so that the port
* can be retried on the next phy event. This risks
* repeated EH runs but seems to be a better tradeoff than
* shutting down a port after a botched hotplug attempt.
*/
if (ata_is_host_link(link))
ata_eh_thaw_port(ap);
goto out;
}
now = jiffies;
if (time_before(now, deadline)) {
unsigned long delta = deadline - now;
ata_link_warn(failed_link,
"reset failed (errno=%d), retrying in %u secs\n",
rc, DIV_ROUND_UP(jiffies_to_msecs(delta), 1000));
ata_eh_release(ap);
while (delta)
delta = schedule_timeout_uninterruptible(delta);
ata_eh_acquire(ap);
}
/*
* While disks spinup behind PMP, some controllers fail sending SRST.
* They need to be reset - as well as the PMP - before retrying.
*/
if (rc == -ERESTART) {
if (ata_is_host_link(link))
ata_eh_thaw_port(ap);
goto out;
}
if (try == max_tries - 1) {
sata_down_spd_limit(link, 0);
if (slave)
sata_down_spd_limit(slave, 0);
} else if (rc == -EPIPE)
sata_down_spd_limit(failed_link, 0);
if (hardreset)
reset = hardreset;
goto retry;
}
static inline void ata_eh_pull_park_action(struct ata_port *ap)
{
struct ata_link *link;
struct ata_device *dev;
unsigned long flags;
/*
* This function can be thought of as an extended version of
* ata_eh_about_to_do() specially crafted to accommodate the
* requirements of ATA_EH_PARK handling. Since the EH thread
* does not leave the do {} while () loop in ata_eh_recover as
* long as the timeout for a park request to *one* device on
* the port has not expired, and since we still want to pick
* up park requests to other devices on the same port or
* timeout updates for the same device, we have to pull
* ATA_EH_PARK actions from eh_info into eh_context.i
* ourselves at the beginning of each pass over the loop.
*
* Additionally, all write accesses to &ap->park_req_pending
* through reinit_completion() (see below) or complete_all()
* (see ata_scsi_park_store()) are protected by the host lock.
* As a result we have that park_req_pending.done is zero on
* exit from this function, i.e. when ATA_EH_PARK actions for
* *all* devices on port ap have been pulled into the
* respective eh_context structs. If, and only if,
* park_req_pending.done is non-zero by the time we reach
* wait_for_completion_timeout(), another ATA_EH_PARK action
* has been scheduled for at least one of the devices on port
* ap and we have to cycle over the do {} while () loop in
* ata_eh_recover() again.
*/
spin_lock_irqsave(ap->lock, flags);
reinit_completion(&ap->park_req_pending);
ata_for_each_link(link, ap, EDGE) {
ata_for_each_dev(dev, link, ALL) {
struct ata_eh_info *ehi = &link->eh_info;
link->eh_context.i.dev_action[dev->devno] |=
ehi->dev_action[dev->devno] & ATA_EH_PARK;
ata_eh_clear_action(link, dev, ehi, ATA_EH_PARK);
}
}
spin_unlock_irqrestore(ap->lock, flags);
}
static void ata_eh_park_issue_cmd(struct ata_device *dev, int park)
{
struct ata_eh_context *ehc = &dev->link->eh_context;
struct ata_taskfile tf;
unsigned int err_mask;
ata_tf_init(dev, &tf);
if (park) {
ehc->unloaded_mask |= 1 << dev->devno;
tf.command = ATA_CMD_IDLEIMMEDIATE;
tf.feature = 0x44;
tf.lbal = 0x4c;
tf.lbam = 0x4e;
tf.lbah = 0x55;
} else {
ehc->unloaded_mask &= ~(1 << dev->devno);
tf.command = ATA_CMD_CHK_POWER;
}
tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
tf.protocol |= ATA_PROT_NODATA;
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
if (park && (err_mask || tf.lbal != 0xc4)) {
ata_dev_err(dev, "head unload failed!\n");
ehc->unloaded_mask &= ~(1 << dev->devno);
}
}
static int ata_eh_revalidate_and_attach(struct ata_link *link,
struct ata_device **r_failed_dev)
{
struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev;
unsigned int new_mask = 0;
unsigned long flags;
int rc = 0;
DPRINTK("ENTER\n");
/* For PATA drive side cable detection to work, IDENTIFY must
* be done backwards such that PDIAG- is released by the slave
* device before the master device is identified.
*/
ata_for_each_dev(dev, link, ALL_REVERSE) {
unsigned int action = ata_eh_dev_action(dev);
unsigned int readid_flags = 0;
if (ehc->i.flags & ATA_EHI_DID_RESET)
readid_flags |= ATA_READID_POSTRESET;
if ((action & ATA_EH_REVALIDATE) && ata_dev_enabled(dev)) {
WARN_ON(dev->class == ATA_DEV_PMP);
if (ata_phys_link_offline(ata_dev_phys_link(dev))) {
rc = -EIO;
goto err;
}
ata_eh_about_to_do(link, dev, ATA_EH_REVALIDATE);
rc = ata_dev_revalidate(dev, ehc->classes[dev->devno],
readid_flags);
if (rc)
goto err;
ata_eh_done(link, dev, ATA_EH_REVALIDATE);
/* Configuration may have changed, reconfigure
* transfer mode.
*/
ehc->i.flags |= ATA_EHI_SETMODE;
/* schedule the scsi_rescan_device() here */
schedule_work(&(ap->scsi_rescan_task));
} else if (dev->class == ATA_DEV_UNKNOWN &&
ehc->tries[dev->devno] &&
ata_class_enabled(ehc->classes[dev->devno])) {
/* Temporarily set dev->class, it will be
* permanently set once all configurations are
* complete. This is necessary because new
* device configuration is done in two
* separate loops.
*/
dev->class = ehc->classes[dev->devno];
if (dev->class == ATA_DEV_PMP)
rc = sata_pmp_attach(dev);
else
rc = ata_dev_read_id(dev, &dev->class,
readid_flags, dev->id);
/* read_id might have changed class, store and reset */
ehc->classes[dev->devno] = dev->class;
dev->class = ATA_DEV_UNKNOWN;
switch (rc) {
case 0:
/* clear error info accumulated during probe */
ata_ering_clear(&dev->ering);
new_mask |= 1 << dev->devno;
break;
case -ENOENT:
/* IDENTIFY was issued to non-existent
* device. No need to reset. Just
* thaw and ignore the device.
*/
ata_eh_thaw_port(ap);
break;
default:
goto err;
}
}
}
/* PDIAG- should have been released, ask cable type if post-reset */
if ((ehc->i.flags & ATA_EHI_DID_RESET) && ata_is_host_link(link)) {
if (ap->ops->cable_detect)
ap->cbl = ap->ops->cable_detect(ap);
ata_force_cbl(ap);
}
/* Configure new devices forward such that user doesn't see
* device detection messages backwards.
*/
ata_for_each_dev(dev, link, ALL) {
if (!(new_mask & (1 << dev->devno)))
continue;
dev->class = ehc->classes[dev->devno];
if (dev->class == ATA_DEV_PMP)
continue;
ehc->i.flags |= ATA_EHI_PRINTINFO;
rc = ata_dev_configure(dev);
ehc->i.flags &= ~ATA_EHI_PRINTINFO;
if (rc) {
dev->class = ATA_DEV_UNKNOWN;
goto err;
}
spin_lock_irqsave(ap->lock, flags);
ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG;
spin_unlock_irqrestore(ap->lock, flags);
/* new device discovered, configure xfermode */
ehc->i.flags |= ATA_EHI_SETMODE;
}
return 0;
err:
*r_failed_dev = dev;
DPRINTK("EXIT rc=%d\n", rc);
return rc;
}
/**
* ata_set_mode - Program timings and issue SET FEATURES - XFER
* @link: link on which timings will be programmed
* @r_failed_dev: out parameter for failed device
*
* Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If
* ata_set_mode() fails, pointer to the failing device is
* returned in @r_failed_dev.
*
* LOCKING:
* PCI/etc. bus probe sem.
*
* RETURNS:
* 0 on success, negative errno otherwise
*/
int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
{
struct ata_port *ap = link->ap;
struct ata_device *dev;
int rc;
/* if data transfer is verified, clear DUBIOUS_XFER on ering top */
ata_for_each_dev(dev, link, ENABLED) {
if (!(dev->flags & ATA_DFLAG_DUBIOUS_XFER)) {
struct ata_ering_entry *ent;
ent = ata_ering_top(&dev->ering);
if (ent)
ent->eflags &= ~ATA_EFLAG_DUBIOUS_XFER;
}
}
/* has private set_mode? */
if (ap->ops->set_mode)
rc = ap->ops->set_mode(link, r_failed_dev);
else
rc = ata_do_set_mode(link, r_failed_dev);
/* if transfer mode has changed, set DUBIOUS_XFER on device */
ata_for_each_dev(dev, link, ENABLED) {
struct ata_eh_context *ehc = &link->eh_context;
u8 saved_xfer_mode = ehc->saved_xfer_mode[dev->devno];
u8 saved_ncq = !!(ehc->saved_ncq_enabled & (1 << dev->devno));
if (dev->xfer_mode != saved_xfer_mode ||
ata_ncq_enabled(dev) != saved_ncq)
dev->flags |= ATA_DFLAG_DUBIOUS_XFER;
}
return rc;
}
/**
* atapi_eh_clear_ua - Clear ATAPI UNIT ATTENTION after reset
* @dev: ATAPI device to clear UA for
*
* Resets and other operations can make an ATAPI device raise
* UNIT ATTENTION which causes the next operation to fail. This
* function clears UA.
*
* LOCKING:
* EH context (may sleep).
*
* RETURNS:
* 0 on success, -errno on failure.
*/
static int atapi_eh_clear_ua(struct ata_device *dev)
{
int i;
for (i = 0; i < ATA_EH_UA_TRIES; i++) {
u8 *sense_buffer = dev->link->ap->sector_buf;
u8 sense_key = 0;
unsigned int err_mask;
err_mask = atapi_eh_tur(dev, &sense_key);
if (err_mask != 0 && err_mask != AC_ERR_DEV) {
ata_dev_warn(dev,
"TEST_UNIT_READY failed (err_mask=0x%x)\n",
err_mask);
return -EIO;
}
if (!err_mask || sense_key != UNIT_ATTENTION)
return 0;
err_mask = atapi_eh_request_sense(dev, sense_buffer, sense_key);
if (err_mask) {
ata_dev_warn(dev, "failed to clear "
"UNIT ATTENTION (err_mask=0x%x)\n", err_mask);
return -EIO;
}
}
ata_dev_warn(dev, "UNIT ATTENTION persists after %d tries\n",
ATA_EH_UA_TRIES);
return 0;
}
/**
* ata_eh_maybe_retry_flush - Retry FLUSH if necessary
* @dev: ATA device which may need FLUSH retry
*
* If @dev failed FLUSH, it needs to be reported upper layer
* immediately as it means that @dev failed to remap and already
* lost at least a sector and further FLUSH retrials won't make
* any difference to the lost sector. However, if FLUSH failed
* for other reasons, for example transmission error, FLUSH needs
* to be retried.
*
* This function determines whether FLUSH failure retry is
* necessary and performs it if so.
*
* RETURNS:
* 0 if EH can continue, -errno if EH needs to be repeated.
*/
static int ata_eh_maybe_retry_flush(struct ata_device *dev)
{
struct ata_link *link = dev->link;
struct ata_port *ap = link->ap;
struct ata_queued_cmd *qc;
struct ata_taskfile tf;
unsigned int err_mask;
int rc = 0;
/* did flush fail for this device? */
if (!ata_tag_valid(link->active_tag))
return 0;
qc = __ata_qc_from_tag(ap, link->active_tag);
if (qc->dev != dev || (qc->tf.command != ATA_CMD_FLUSH_EXT &&
qc->tf.command != ATA_CMD_FLUSH))
return 0;
/* if the device failed it, it should be reported to upper layers */
if (qc->err_mask & AC_ERR_DEV)
return 0;
/* flush failed for some other reason, give it another shot */
ata_tf_init(dev, &tf);
tf.command = qc->tf.command;
tf.flags |= ATA_TFLAG_DEVICE;
tf.protocol = ATA_PROT_NODATA;
ata_dev_warn(dev, "retrying FLUSH 0x%x Emask 0x%x\n",
tf.command, qc->err_mask);
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
if (!err_mask) {
/*
* FLUSH is complete but there's no way to
* successfully complete a failed command from EH.
* Making sure retry is allowed at least once and
* retrying it should do the trick - whatever was in
* the cache is already on the platter and this won't
* cause infinite loop.
*/
qc->scsicmd->allowed = max(qc->scsicmd->allowed, 1);
} else {
ata_dev_warn(dev, "FLUSH failed Emask 0x%x\n",
err_mask);
rc = -EIO;
/* if device failed it, report it to upper layers */
if (err_mask & AC_ERR_DEV) {
qc->err_mask |= AC_ERR_DEV;
qc->result_tf = tf;
if (!(ap->pflags & ATA_PFLAG_FROZEN))
rc = 0;
}
}
return rc;
}
/**
* ata_eh_set_lpm - configure SATA interface power management
* @link: link to configure power management
* @policy: the link power management policy
* @r_failed_dev: out parameter for failed device
*
* Enable SATA Interface power management. This will enable
* Device Interface Power Management (DIPM) for min_power
* policy, and then call driver specific callbacks for
* enabling Host Initiated Power management.
*
* LOCKING:
* EH context.
*
* RETURNS:
* 0 on success, -errno on failure.
*/
static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
struct ata_device **r_failed_dev)
{
struct ata_port *ap = ata_is_host_link(link) ? link->ap : NULL;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL;
enum ata_lpm_policy old_policy = link->lpm_policy;
bool no_dipm = link->ap->flags & ATA_FLAG_NO_DIPM;
unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM;
unsigned int err_mask;
int rc;
/* if the link or host doesn't do LPM, noop */
if ((link->flags & ATA_LFLAG_NO_LPM) || (ap && !ap->ops->set_lpm))
return 0;
/*
* DIPM is enabled only for MIN_POWER as some devices
* misbehave when the host NACKs transition to SLUMBER. Order
* device and link configurations such that the host always
* allows DIPM requests.
*/
ata_for_each_dev(dev, link, ENABLED) {
bool hipm = ata_id_has_hipm(dev->id);
bool dipm = ata_id_has_dipm(dev->id) && !no_dipm;
/* find the first enabled and LPM enabled devices */
if (!link_dev)
link_dev = dev;
if (!lpm_dev && (hipm || dipm))
lpm_dev = dev;
hints &= ~ATA_LPM_EMPTY;
if (!hipm)
hints &= ~ATA_LPM_HIPM;
/* disable DIPM before changing link config */
if (policy != ATA_LPM_MIN_POWER && dipm) {
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_DISABLE, SATA_DIPM);
if (err_mask && err_mask != AC_ERR_DEV) {
ata_dev_warn(dev,
"failed to disable DIPM, Emask 0x%x\n",
err_mask);
rc = -EIO;
goto fail;
}
}
}
if (ap) {
rc = ap->ops->set_lpm(link, policy, hints);
if (!rc && ap->slave_link)
rc = ap->ops->set_lpm(ap->slave_link, policy, hints);
} else
rc = sata_pmp_set_lpm(link, policy, hints);
/*
* Attribute link config failure to the first (LPM) enabled
* device on the link.
*/
if (rc) {
if (rc == -EOPNOTSUPP) {
link->flags |= ATA_LFLAG_NO_LPM;
return 0;
}
dev = lpm_dev ? lpm_dev : link_dev;
goto fail;
}
/*
* Low level driver acked the transition. Issue DIPM command
* with the new policy set.
*/
link->lpm_policy = policy;
if (ap && ap->slave_link)
ap->slave_link->lpm_policy = policy;
/* host config updated, enable DIPM if transitioning to MIN_POWER */
ata_for_each_dev(dev, link, ENABLED) {
if (policy == ATA_LPM_MIN_POWER && !no_dipm &&
ata_id_has_dipm(dev->id)) {
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_ENABLE, SATA_DIPM);
if (err_mask && err_mask != AC_ERR_DEV) {
ata_dev_warn(dev,
"failed to enable DIPM, Emask 0x%x\n",
err_mask);
rc = -EIO;
goto fail;
}
}
}
return 0;
fail:
/* restore the old policy */
link->lpm_policy = old_policy;
if (ap && ap->slave_link)
ap->slave_link->lpm_policy = old_policy;
/* if no device or only one more chance is left, disable LPM */
if (!dev || ehc->tries[dev->devno] <= 2) {
ata_link_warn(link, "disabling LPM on the link\n");
link->flags |= ATA_LFLAG_NO_LPM;
}
if (r_failed_dev)
*r_failed_dev = dev;
return rc;
}
int ata_link_nr_enabled(struct ata_link *link)
{
struct ata_device *dev;
int cnt = 0;
ata_for_each_dev(dev, link, ENABLED)
cnt++;
return cnt;
}
static int ata_link_nr_vacant(struct ata_link *link)
{
struct ata_device *dev;
int cnt = 0;
ata_for_each_dev(dev, link, ALL)
if (dev->class == ATA_DEV_UNKNOWN)
cnt++;
return cnt;
}
static int ata_eh_skip_recovery(struct ata_link *link)
{
struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev;
/* skip disabled links */
if (link->flags & ATA_LFLAG_DISABLED)
return 1;
/* skip if explicitly requested */
if (ehc->i.flags & ATA_EHI_NO_RECOVERY)
return 1;
/* thaw frozen port and recover failed devices */
if ((ap->pflags & ATA_PFLAG_FROZEN) || ata_link_nr_enabled(link))
return 0;
/* reset at least once if reset is requested */
if ((ehc->i.action & ATA_EH_RESET) &&
!(ehc->i.flags & ATA_EHI_DID_RESET))
return 0;
/* skip if class codes for all vacant slots are ATA_DEV_NONE */
ata_for_each_dev(dev, link, ALL) {
if (dev->class == ATA_DEV_UNKNOWN &&
ehc->classes[dev->devno] != ATA_DEV_NONE)
return 0;
}
return 1;
}
static int ata_count_probe_trials_cb(struct ata_ering_entry *ent, void *void_arg)
{
u64 interval = msecs_to_jiffies(ATA_EH_PROBE_TRIAL_INTERVAL);
u64 now = get_jiffies_64();
int *trials = void_arg;
if ((ent->eflags & ATA_EFLAG_OLD_ER) ||
(ent->timestamp < now - min(now, interval)))
return -1;
(*trials)++;
return 0;
}
static int ata_eh_schedule_probe(struct ata_device *dev)
{
struct ata_eh_context *ehc = &dev->link->eh_context;
struct ata_link *link = ata_dev_phys_link(dev);
int trials = 0;
if (!(ehc->i.probe_mask & (1 << dev->devno)) ||
(ehc->did_probe_mask & (1 << dev->devno)))
return 0;
ata_eh_detach_dev(dev);
ata_dev_init(dev);
ehc->did_probe_mask |= (1 << dev->devno);
ehc->i.action |= ATA_EH_RESET;
ehc->saved_xfer_mode[dev->devno] = 0;
ehc->saved_ncq_enabled &= ~(1 << dev->devno);
/* the link maybe in a deep sleep, wake it up */
if (link->lpm_policy > ATA_LPM_MAX_POWER) {
if (ata_is_host_link(link))
link->ap->ops->set_lpm(link, ATA_LPM_MAX_POWER,
ATA_LPM_EMPTY);
else
sata_pmp_set_lpm(link, ATA_LPM_MAX_POWER,
ATA_LPM_EMPTY);
}
/* Record and count probe trials on the ering. The specific
* error mask used is irrelevant. Because a successful device
* detection clears the ering, this count accumulates only if
* there are consecutive failed probes.
*
* If the count is equal to or higher than ATA_EH_PROBE_TRIALS
* in the last ATA_EH_PROBE_TRIAL_INTERVAL, link speed is
* forced to 1.5Gbps.
*
* This is to work around cases where failed link speed
* negotiation results in device misdetection leading to
* infinite DEVXCHG or PHRDY CHG events.
*/
ata_ering_record(&dev->ering, 0, AC_ERR_OTHER);
ata_ering_map(&dev->ering, ata_count_probe_trials_cb, &trials);
if (trials > ATA_EH_PROBE_TRIALS)
sata_down_spd_limit(link, 1);
return 1;
}
static int ata_eh_handle_dev_fail(struct ata_device *dev, int err)
{
struct ata_eh_context *ehc = &dev->link->eh_context;
/* -EAGAIN from EH routine indicates retry without prejudice.
* The requester is responsible for ensuring forward progress.
*/
if (err != -EAGAIN)
ehc->tries[dev->devno]--;
switch (err) {
case -ENODEV:
/* device missing or wrong IDENTIFY data, schedule probing */
ehc->i.probe_mask |= (1 << dev->devno);
case -EINVAL:
/* give it just one more chance */
ehc->tries[dev->devno] = min(ehc->tries[dev->devno], 1);
case -EIO:
if (ehc->tries[dev->devno] == 1) {
/* This is the last chance, better to slow
* down than lose it.
*/
sata_down_spd_limit(ata_dev_phys_link(dev), 0);
if (dev->pio_mode > XFER_PIO_0)
ata_down_xfermask_limit(dev, ATA_DNXFER_PIO);
}
}
if (ata_dev_enabled(dev) && !ehc->tries[dev->devno]) {
/* disable device if it has used up all its chances */
ata_dev_disable(dev);
/* detach if offline */
if (ata_phys_link_offline(ata_dev_phys_link(dev)))
ata_eh_detach_dev(dev);
/* schedule probe if necessary */
if (ata_eh_schedule_probe(dev)) {
ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
memset(ehc->cmd_timeout_idx[dev->devno], 0,
sizeof(ehc->cmd_timeout_idx[dev->devno]));
}
return 1;
} else {
ehc->i.action |= ATA_EH_RESET;
return 0;
}
}
/**
* ata_eh_recover - recover host port after error
* @ap: host port to recover
* @prereset: prereset method (can be NULL)
* @softreset: softreset method (can be NULL)
* @hardreset: hardreset method (can be NULL)
* @postreset: postreset method (can be NULL)
* @r_failed_link: out parameter for failed link
*
* This is the alpha and omega, eum and yang, heart and soul of
* libata exception handling. On entry, actions required to
* recover each link and hotplug requests are recorded in the
* link's eh_context. This function executes all the operations
* with appropriate retrials and fallbacks to resurrect failed
* devices, detach goners and greet newcomers.
*
* LOCKING:
* Kernel thread context (may sleep).
*
* RETURNS:
* 0 on success, -errno on failure.
*/
int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
ata_postreset_fn_t postreset,
struct ata_link **r_failed_link)
{
struct ata_link *link;
struct ata_device *dev;
int rc, nr_fails;
unsigned long flags, deadline;
DPRINTK("ENTER\n");
/* prep for recovery */
ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
dev_info(ap->host->dev, "ata_eh_recover(), ehc->i.action=0x%08x\n",ehc->i.action);
/* re-enable link? */
if (ehc->i.action & ATA_EH_ENABLE_LINK) {
ata_eh_about_to_do(link, NULL, ATA_EH_ENABLE_LINK);
spin_lock_irqsave(ap->lock, flags);
link->flags &= ~ATA_LFLAG_DISABLED;
spin_unlock_irqrestore(ap->lock, flags);
ata_eh_done(link, NULL, ATA_EH_ENABLE_LINK);
}
ata_for_each_dev(dev, link, ALL) {
dev_info(ap->host->dev, "ata_eh_recover(), link->flags=0x%08x\n",link->flags);
if (link->flags & ATA_LFLAG_NO_RETRY)
ehc->tries[dev->devno] = 1;
else
ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
/* collect port action mask recorded in dev actions */
ehc->i.action |= ehc->i.dev_action[dev->devno] &
~ATA_EH_PERDEV_MASK;
ehc->i.dev_action[dev->devno] &= ATA_EH_PERDEV_MASK;
/* process hotplug request */
if (dev->flags & ATA_DFLAG_DETACH)
ata_eh_detach_dev(dev);
/* schedule probe if necessary */
if (!ata_dev_enabled(dev))
ata_eh_schedule_probe(dev);
}
}
retry:
rc = 0;
/* if UNLOADING, finish immediately */
if (ap->pflags & ATA_PFLAG_UNLOADING)
goto out;
/* prep for EH */
ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
/* skip EH if possible. */
if (ata_eh_skip_recovery(link))
ehc->i.action = 0;
ata_for_each_dev(dev, link, ALL)
ehc->classes[dev->devno] = ATA_DEV_UNKNOWN;
}
/* reset */
ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
if (!(ehc->i.action & ATA_EH_RESET))
continue;
rc = ata_eh_reset(link, ata_link_nr_vacant(link),
prereset, softreset, hardreset, postreset);
if (rc) {
ata_link_err(link, "reset failed, giving up\n");
goto out;
}
}
do {
unsigned long now;
/*
* clears ATA_EH_PARK in eh_info and resets
* ap->park_req_pending
*/
ata_eh_pull_park_action(ap);
deadline = jiffies;
ata_for_each_link(link, ap, EDGE) {
ata_for_each_dev(dev, link, ALL) {
struct ata_eh_context *ehc = &link->eh_context;
unsigned long tmp;
if (dev->class != ATA_DEV_ATA &&
dev->class != ATA_DEV_ZAC)
continue;
if (!(ehc->i.dev_action[dev->devno] &
ATA_EH_PARK))
continue;
tmp = dev->unpark_deadline;
if (time_before(deadline, tmp))
deadline = tmp;
else if (time_before_eq(tmp, jiffies))
continue;
if (ehc->unloaded_mask & (1 << dev->devno))
continue;
ata_eh_park_issue_cmd(dev, 1);
}
}
now = jiffies;
if (time_before_eq(deadline, now))
break;
ata_eh_release(ap);
deadline = wait_for_completion_timeout(&ap->park_req_pending,
deadline - now);
ata_eh_acquire(ap);
} while (deadline);
ata_for_each_link(link, ap, EDGE) {
ata_for_each_dev(dev, link, ALL) {
if (!(link->eh_context.unloaded_mask &
(1 << dev->devno)))
continue;
ata_eh_park_issue_cmd(dev, 0);
ata_eh_done(link, dev, ATA_EH_PARK);
}
}
/* the rest */
nr_fails = 0;
ata_for_each_link(link, ap, PMP_FIRST) {
struct ata_eh_context *ehc = &link->eh_context;
if (sata_pmp_attached(ap) && ata_is_host_link(link))
goto config_lpm;
/* revalidate existing devices and attach new ones */
rc = ata_eh_revalidate_and_attach(link, &dev);
if (rc)
goto rest_fail;
/* if PMP got attached, return, pmp EH will take care of it */
if (link->device->class == ATA_DEV_PMP) {
ehc->i.action = 0;
return 0;
}
/* configure transfer mode if necessary */
if (ehc->i.flags & ATA_EHI_SETMODE) {
rc = ata_set_mode(link, &dev);
if (rc)
goto rest_fail;
ehc->i.flags &= ~ATA_EHI_SETMODE;
}
/* If reset has been issued, clear UA to avoid
* disrupting the current users of the device.
*/
if (ehc->i.flags & ATA_EHI_DID_RESET) {
ata_for_each_dev(dev, link, ALL) {
if (dev->class != ATA_DEV_ATAPI)
continue;
rc = atapi_eh_clear_ua(dev);
if (rc)
goto rest_fail;
if (zpodd_dev_enabled(dev))
zpodd_post_poweron(dev);
}
}
/* retry flush if necessary */
ata_for_each_dev(dev, link, ALL) {
if (dev->class != ATA_DEV_ATA &&
dev->class != ATA_DEV_ZAC)
continue;
rc = ata_eh_maybe_retry_flush(dev);
if (rc)
goto rest_fail;
}
config_lpm:
/* configure link power saving */
if (link->lpm_policy != ap->target_lpm_policy) {
rc = ata_eh_set_lpm(link, ap->target_lpm_policy, &dev);
if (rc)
goto rest_fail;
}
/* this link is okay now */
ehc->i.flags = 0;
continue;
rest_fail:
nr_fails++;
if (dev)
ata_eh_handle_dev_fail(dev, rc);
if (ap->pflags & ATA_PFLAG_FROZEN) {
/* PMP reset requires working host port.
* Can't retry if it's frozen.
*/
if (sata_pmp_attached(ap))
goto out;
break;
}
}
if (nr_fails)
goto retry;
out:
if (rc && r_failed_link)
*r_failed_link = link;
DPRINTK("EXIT, rc=%d\n", rc);
return rc;
}
/**
* ata_eh_finish - finish up EH
* @ap: host port to finish EH for
*
* Recovery is complete. Clean up EH states and retry or finish
* failed qcs.
*
* LOCKING:
* None.
*/
void ata_eh_finish(struct ata_port *ap)
{
int tag;
/* retry or finish qcs */
for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag);
if (!(qc->flags & ATA_QCFLAG_FAILED))
continue;
if (qc->err_mask) {
/* FIXME: Once EH migration is complete,
* generate sense data in this function,
* considering both err_mask and tf.
*/
if (qc->flags & ATA_QCFLAG_RETRY)
ata_eh_qc_retry(qc);
else
ata_eh_qc_complete(qc);
} else {
if (qc->flags & ATA_QCFLAG_SENSE_VALID) {
ata_eh_qc_complete(qc);
} else {
/* feed zero TF to sense generation */
memset(&qc->result_tf, 0, sizeof(qc->result_tf));
ata_eh_qc_retry(qc);
}
}
}
/* make sure nr_active_links is zero after EH */
WARN_ON(ap->nr_active_links);
ap->nr_active_links = 0;
}
/**
* ata_do_eh - do standard error handling
* @ap: host port to handle error for
*
* @prereset: prereset method (can be NULL)
* @softreset: softreset method (can be NULL)
* @hardreset: hardreset method (can be NULL)
* @postreset: postreset method (can be NULL)
*
* Perform standard error handling sequence.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset,
ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
ata_postreset_fn_t postreset)
{
struct ata_device *dev;
int rc;
dev_info(ap->host->dev, "libata-eh.c ata_do_eh()");
ata_eh_autopsy(ap);
ata_eh_report(ap);
rc = ata_eh_recover(ap, prereset, softreset, hardreset, postreset,
NULL);
if (rc) {
ata_for_each_dev(dev, &ap->link, ALL)
ata_dev_disable(dev);
}
ata_eh_finish(ap);
}
/**
* ata_std_error_handler - standard error handler
* @ap: host port to handle error for
*
* Standard error handler
*
* LOCKING:
* Kernel thread context (may sleep).
*/
void ata_std_error_handler(struct ata_port *ap)
{
struct ata_port_operations *ops = ap->ops;
ata_reset_fn_t hardreset = ops->hardreset;
/* ignore built-in hardreset if SCR access is not available */
if (hardreset == sata_std_hardreset && !sata_scr_valid(&ap->link))
hardreset = NULL;
dev_info(ap->host->dev, "libata-eh.c ata_std_error_handler()");
ata_do_eh(ap, ops->prereset, ops->softreset, hardreset, ops->postreset);
}
#ifdef CONFIG_PM
/**
* ata_eh_handle_port_suspend - perform port suspend operation
* @ap: port to suspend
*
* Suspend @ap.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
static void ata_eh_handle_port_suspend(struct ata_port *ap)
{
unsigned long flags;
int rc = 0;
struct ata_device *dev;
dev_info(ap->host->dev, "ata_eh_handle_port_suspend()\n");
/* are we suspending? */
spin_lock_irqsave(ap->lock, flags);
if (!(ap->pflags & ATA_PFLAG_PM_PENDING) ||
ap->pm_mesg.event & PM_EVENT_RESUME) {
spin_unlock_irqrestore(ap->lock, flags);
return;
}
spin_unlock_irqrestore(ap->lock, flags);
WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED);
/*
* If we have a ZPODD attached, check its zero
* power ready status before the port is frozen.
* Only needed for runtime suspend.
*/
if (PMSG_IS_AUTO(ap->pm_mesg)) {
ata_for_each_dev(dev, &ap->link, ENABLED) {
if (zpodd_dev_enabled(dev))
zpodd_on_suspend(dev);
}
}
/* tell ACPI we're suspending */
rc = ata_acpi_on_suspend(ap);
if (rc)
goto out;
/* suspend */
ata_eh_freeze_port(ap);
if (ap->ops->port_suspend)
rc = ap->ops->port_suspend(ap, ap->pm_mesg);
ata_acpi_set_state(ap, ap->pm_mesg);
out:
/* update the flags */
spin_lock_irqsave(ap->lock, flags);
ap->pflags &= ~ATA_PFLAG_PM_PENDING;
if (rc == 0)
ap->pflags |= ATA_PFLAG_SUSPENDED;
else if (ap->pflags & ATA_PFLAG_FROZEN)
ata_port_schedule_eh(ap);
spin_unlock_irqrestore(ap->lock, flags);
return;
}
/**
* ata_eh_handle_port_resume - perform port resume operation
* @ap: port to resume
*
* Resume @ap.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
static void ata_eh_handle_port_resume(struct ata_port *ap)
{
struct ata_link *link;
struct ata_device *dev;
unsigned long flags;
int rc = 0;
/* are we resuming? */
spin_lock_irqsave(ap->lock, flags);
if (!(ap->pflags & ATA_PFLAG_PM_PENDING) ||
!(ap->pm_mesg.event & PM_EVENT_RESUME)) {
spin_unlock_irqrestore(ap->lock, flags);
return;
}
spin_unlock_irqrestore(ap->lock, flags);
WARN_ON(!(ap->pflags & ATA_PFLAG_SUSPENDED));
/*
* Error timestamps are in jiffies which doesn't run while
* suspended and PHY events during resume isn't too uncommon.
* When the two are combined, it can lead to unnecessary speed
* downs if the machine is suspended and resumed repeatedly.
* Clear error history.
*/
ata_for_each_link(link, ap, HOST_FIRST)
ata_for_each_dev(dev, link, ALL)
ata_ering_clear(&dev->ering);
ata_acpi_set_state(ap, ap->pm_mesg);
if (ap->ops->port_resume)
rc = ap->ops->port_resume(ap);
/* tell ACPI that we're resuming */
ata_acpi_on_resume(ap);
/* update the flags */
spin_lock_irqsave(ap->lock, flags);
ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED);
spin_unlock_irqrestore(ap->lock, flags);
}
#endif /* CONFIG_PM */
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/clk/ 0000775 0000000 0000000 00000000000 12666203034 0024215 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/clk/clk-si5338.c 0000664 0000000 0000000 00000550572 12666203034 0026104 0 ustar 00root root 0000000 0000000 /*!***************************************************************************
*! FILE NAME : si5338.c
*! DESCRIPTION: control of the Silicon Laboratories SI5338 clock generator
*! Copyright (C) 2013 Elphel, 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 3 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 .
*/
#define DEBUG /* should be before linux/module.h - enables dev_dbg at boot in this file (needs "debug" in bootarg)*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#undef GENERATE_EXTRA
#define DRV_VERSION "1.0"
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
#define REG5338_PAGE 255
#define REG5338_PAGE_MASK 1
#define REG5338_DEV_CONFIG2 2
#define REG5338_DEV_CONFIG2_MASK 0x3f
#define REG5338_DEV_CONFIG2_VAL 38 /* last 2 digits of part number */
#define LAST_REG 347
#define FVCOMIN 2200000000LL
#define FVCOMAX 2840000000LL
#define INFREQMIN 5000000LL
#define INFREQMAX 710000000LL
#define INFREQDIV 40000000LL /* divide input frequency if above */
#define SPREAD_RATE_MIN 31500 /* 31.5 KHz */
#define SPREAD_RATE_MAX 63000 /* 63 KHz */
#define SPREAD_AMP_MIN 10 /* 0.1% */
#define SPREAD_AMP_MAX 500 /* 5.0% */
#define SPREAD_AMP_DENOM 10000 /* 0.01% amplitude step */
#define SPREAD_RATE_DFLT 31500 /* 31.5 KHz */
#define SPREAD_AMP_DFLT 50 /* 0.5% */
#define MSINT_MIN 8 /* not considering 4,6 */
#define MSINT_MAX 567
#define INIT_TIMEOUT 1000 /* reads of the I2C status register (1 cycle ~ 0.1 ms) */
#define AWE_IN_MUX 0x1d18
#define AWE_IN_MUX1 0x1c1c
#define AWE_FB_MUX 0x1e18
#define AWE_FB_MUX1 0x1c20
#define AWE_XTAL_FREQ 0x1c03
#define AWE_PFD_REF 0x1de0
#define AWE_PFD_FB 0x1ee0
#define AWE_P1DIV 0x1d07
#define AWE_P2DIV 0x1e07
#define AWE_DRV0_PDN 0x1f01
#define AWE_MS0_PDN 0x1f02
#define AWE_R0DIV 0x1f1c
#define AWE_R0DIV_IN 0x1fe0
#define AWE_DRV1_PDN 0x2001
#define AWE_MS1_PDN 0x2002
#define AWE_R1DIV 0x201c
#define AWE_R1DIV_IN 0x20e0
#define AWE_DRV2_PDN 0x2101
#define AWE_MS2_PDN 0x2102
#define AWE_R2DIV 0x211c
#define AWE_R2DIV_IN 0x21e0
#define AWE_DRV3_PDN 0x2201
#define AWE_MS3_PDN 0x2202
#define AWE_R3DIV 0x221c
#define AWE_R3DIV_IN 0x22e0
#define AWE_DRV0_VDDO 0x2303
#define AWE_DRV1_VDDO 0x230c
#define AWE_DRV2_VDDO 0x2330
#define AWE_DRV3_VDDO 0x23c0
#define AWE_DRV0_FMT 0x2407
#define AWE_DRV0_INV 0x2418
#define AWE_DRV1_FMT 0x2507
#define AWE_DRV1_INV 0x2518
#define AWE_DRV2_FMT 0x2607
#define AWE_DRV2_INV 0x2618
#define AWE_DRV3_FMT 0x2707
#define AWE_DRV3_INV 0x2718
#define AWE_DRV0_TRIM 0x281f
#define AWE_DRV1_TRIM_A 0x28e0
#define AWE_DRV1_TRIM_B 0x2903
#define AWE_DRV2_TRIM 0x297c
#define AWE_DRV3_TRIM 0x2a1f
#define AWE_FCAL_OVRD_07_00 0x2dff
#define AWE_FCAL_OVRD_15_08 0x2eff
#define AWE_FCAL_OVRD_17_15 0x2f03
#define AWE_REG47_72 0x2ffc
#define AWE_PFD_EXTFB 0x3080
#define AWE_PLL_KPHI 0x307f
#define AWE_FCAL_OVRD_EN 0x3180
#define AWE_VCO_GAIN 0x3170
#define AWE_RSEL 0x310c
#define AWE_BWSEL 0x3103
#define AWE_VCO_GAIN_RSEL_BWSEL 0x317f
#define AWE_PLL_EN 0x32c0
#define AWE_MSCAL 0x323f
#define AWE_MS3_HS 0x3380
#define AWE_MS2_HS 0x3340
#define AWE_MS1_HS 0x3320
#define AWE_MS0_HS 0x3310
#define AWE_MS_PEC 0x3307
#define AWE_MS0_FIDCT 0x3460
#define AWE_MS0_FIDDIS 0x3410
#define AWE_MS0_SSMODE 0x340C
#define AWE_MS0_PHIDCT 0x3403
#define AWE_MS0_P1_07_00 0x35ff
#define AWE_MS0_P1_15_08 0x36ff
#define AWE_MS0_P1_17_16 0x3703
#define AWE_MS0_P2_05_00 0x37fc
#define AWE_MS0_P2_13_06 0x38ff
#define AWE_MS0_P2_21_14 0x39ff
#define AWE_MS0_P2_29_22 0x3aff
#define AWE_MS0_P3_07_00 0x3bff
#define AWE_MS0_P3_15_08 0x3cff
#define AWE_MS0_P3_23_16 0x3dff
#define AWE_MS0_P3_29_24 0x3e3f
#define AWE_MS1_FIDCT 0x3f60
#define AWE_MS1_FIDDIS 0x3f10
#define AWE_MS1_SSMODE 0x3f0C
#define AWE_MS1_PHIDCT 0x3f03
#define AWE_MS1_P1_07_00 0x40ff
#define AWE_MS1_P1_15_08 0x41ff
#define AWE_MS1_P1_17_16 0x4203
#define AWE_MS1_P2_05_00 0x42fc
#define AWE_MS1_P2_13_06 0x43ff
#define AWE_MS1_P2_21_14 0x44ff
#define AWE_MS1_P2_29_22 0x45ff
#define AWE_MS1_P3_07_00 0x46ff
#define AWE_MS1_P3_15_08 0x47ff
#define AWE_MS1_P3_23_16 0x48ff
#define AWE_MS1_P3_29_24 0x493f
#define AWE_MS2_FRCTL 0x4a60 /* different name? */
#define AWE_MS2_FIDDIS 0x4a10
#define AWE_MS2_SSMODE 0x4a0C
#define AWE_MS2_PHIDCT 0x4a03
#define AWE_MS2_P1_07_00 0x4bff
#define AWE_MS2_P1_15_08 0x4cff
#define AWE_MS2_P1_17_16 0x4d03
#define AWE_MS2_P2_05_00 0x4dfc
#define AWE_MS2_P2_13_06 0x4eff
#define AWE_MS2_P2_21_14 0x4fff
#define AWE_MS2_P2_29_22 0x50ff
#define AWE_MS2_P3_07_00 0x51ff
#define AWE_MS2_P3_15_08 0x52ff
#define AWE_MS2_P3_23_16 0x53ff
#define AWE_MS2_P3_29_24 0x543f
#define AWE_MS3_FIDCT 0x5560
#define AWE_MS3_FIDDIS 0x5510
#define AWE_MS3_SSMODE 0x550C
#define AWE_MS3_PHIDCT 0x5503
#define AWE_MS3_P1_07_00 0x56ff
#define AWE_MS3_P1_15_08 0x57ff
#define AWE_MS3_P1_17_16 0x5803
#define AWE_MS3_P2_05_00 0x58fc
#define AWE_MS3_P2_13_06 0x59ff
#define AWE_MS3_P2_21_14 0x5aff
#define AWE_MS3_P2_29_22 0x5bff
#define AWE_MS3_P3_07_00 0x5cff
#define AWE_MS3_P3_15_08 0x5dff
#define AWE_MS3_P3_23_16 0x5eff
#define AWE_MS3_P3_29_24 0x5f3f
#define AWE_MSN_P1_07_00 0x61ff
#define AWE_MSN_P1_15_08 0x62ff
#define AWE_MSN_P1_17_16 0x6303
#define AWE_MSN_P2_05_00 0x63fc
#define AWE_MSN_P2_13_06 0x64ff
#define AWE_MSN_P2_21_14 0x65ff
#define AWE_MSN_P2_29_22 0x66ff
#define AWE_MSN_P3_07_00 0x67ff
#define AWE_MSN_P3_15_08 0x68ff
#define AWE_MSN_P3_23_16 0x69ff
#define AWE_MSN_P3_29_24 0x6a3f
#define AWE_OUT0_DIS_STATE 0x6ec0
#define AWE_OUT1_DIS_STATE 0x72c0
#define AWE_OUT2_DIS_STATE 0x76c0
#define AWE_OUT3_DIS_STATE 0x7ac0
#define AWE_STATUS 0xdaff
#define AWE_STATUS_PLL_LOL 0xda10
#define AWE_STATUS_PLL_LOS_FDBK 0xda08
#define AWE_STATUS_PLL_LOS_CLKIN 0xda04
#define AWE_STATUS_PLL_SYS_CAL 0xda01
#define AWE_MS_RESET 0xe204
#define AWE_OUT0_DIS 0xe601
#define AWE_OUT1_DIS 0xe602
#define AWE_OUT2_DIS 0xe604
#define AWE_OUT3_DIS 0xe608
#define AWE_OUT_ALL_DIS 0xe610
#define AWE_FCAL_07_00 0xebff
#define AWE_FCAL_15_08 0xecff
#define AWE_FCAL_17_16 0xed03
#define AWE_DIS_LOS 0xf180
#define AWE_REG241 0xf1ff
#define AWE_SOFT_RESET 0xf602
#define AWE_MS0_SSUPP2_07_00 0x11fff
#define AWE_MS0_SSUPP2_14_08 0x1207f
#define AWE_MS0_SSUPP3_07_00 0x121ff /* set them to 0 - default==1 */
#define AWE_MS0_SSUPP3_14_08 0x1227f
#define AWE_MS0_SSUPP1_07_00 0x123ff
#define AWE_MS0_SSUPP1_11_08 0x1240f
#define AWE_MS0_SSUDP1_03_00 0x124f0
#define AWE_MS0_SSUDP1_11_04 0x125ff
#define AWE_MS0_SSDNP2_07_00 0x126ff
#define AWE_MS0_SSDNP2_14_08 0x1277f
#define AWE_MS0_SSDNP3_07_00 0x128ff
#define AWE_MS0_SSDNP3_14_08 0x1297f
#define AWE_MS0_SSDNP1_07_00 0x12aff
#define AWE_MS0_SSDNP1_11_08 0x12b0f
#define AWE_MS1_SSUPP2_07_00 0x12fff
#define AWE_MS1_SSUPP2_14_08 0x1307f
#define AWE_MS1_SSUPP3_07_00 0x131ff
#define AWE_MS1_SSUPP3_14_08 0x1327f
#define AWE_MS1_SSUPP1_07_00 0x133ff
#define AWE_MS1_SSUPP1_11_08 0x1340f
#define AWE_MS1_SSUDP1_03_00 0x134f0
#define AWE_MS1_SSUDP1_11_04 0x135ff
#define AWE_MS1_SSDNP2_07_00 0x136ff
#define AWE_MS1_SSDNP2_14_08 0x1377f
#define AWE_MS1_SSDNP3_07_00 0x138ff
#define AWE_MS1_SSDNP3_14_08 0x1397f
#define AWE_MS1_SSDNP1_07_00 0x13aff
#define AWE_MS1_SSDNP1_11_08 0x13b0f
#define AWE_MS2_SSUPP2_07_00 0x13fff
#define AWE_MS2_SSUPP2_14_08 0x1407f
#define AWE_MS2_SSUPP3_07_00 0x141ff
#define AWE_MS2_SSUPP3_14_08 0x1427f
#define AWE_MS2_SSUPP1_07_00 0x143ff
#define AWE_MS2_SSUPP1_11_08 0x1440f
#define AWE_MS2_SSUDP1_03_00 0x144f0
#define AWE_MS2_SSUDP1_11_04 0x145ff
#define AWE_MS2_SSDNP2_07_00 0x146ff
#define AWE_MS2_SSDNP2_14_08 0x1477f
#define AWE_MS2_SSDNP3_07_00 0x148ff
#define AWE_MS2_SSDNP3_14_08 0x1497f
#define AWE_MS2_SSDNP1_07_00 0x14aff
#define AWE_MS2_SSDNP1_11_08 0x14b0f
#define AWE_MS3_SSUPP2_07_00 0x14fff
#define AWE_MS3_SSUPP2_14_08 0x1507f
#define AWE_MS3_SSUPP3_07_00 0x151ff
#define AWE_MS3_SSUPP3_14_08 0x1527f
#define AWE_MS3_SSUPP1_07_00 0x153ff
#define AWE_MS3_SSUPP1_11_08 0x1540f
#define AWE_MS3_SSUDP1_03_00 0x154f0
#define AWE_MS3_SSUDP1_11_04 0x155ff
#define AWE_MS3_SSDNP2_07_00 0x156ff
#define AWE_MS3_SSDNP2_14_08 0x1577f
#define AWE_MS3_SSDNP3_07_00 0x158ff
#define AWE_MS3_SSDNP3_14_08 0x1597f
#define AWE_MS3_SSDNP1_07_00 0x15aff
#define AWE_MS3_SSDNP1_11_08 0x15b0f
#define AWE_MISC_47 0x2ffc /* write 0x5 */
#define AWE_MISC_106 0x6a80 /* write 0x1 */
#define AWE_MISC_116 0x7480 /* write 0x1 */
#define AWE_MISC_42 0x2a20 /* write 0x1 */
#define AWE_MISC_06A 0x06e0 /* write 0x0 */
#define AWE_MISC_06B 0x0602 /* write 0x0 */
#define AWE_MISC_28 0x1cc0 /* write 0x0 */
#define CACHE_INIT 1
#define CACHE_VOLAT 2
struct si5338_cache_t {
u8 flags;
u8 data;
};
struct si5338_data_t {
u64 input_frequency12;
u64 input_frequency3;
u64 input_frequency4;
u64 input_frequency56;
u32 ss_on_freq_change; /* 0 - disable SS when frequency is changed, 1 - update SS. +2 reset MS after starting SS*/
u32 spread_spectrum_rate[4]; /* in Hz */
u32 spread_spectrum_amp[4]; /* in 0.01% */
// u64 pll_frequency;
int reg_addr; /* used for raw register r/w */
int last_page; /* value of last page accessed (bit 0 of register 255) */
struct mutex lock;
struct si5338_cache_t cache[LAST_REG+1];
};
struct si5338_drv_t {
const char * description;
u8 fmt;
u8 vdd;
u8 trim;
u8 invert; /* bits [1:0} data, [3:2] - don't care ([3]==1 - [1] - any, [2]==1 - [0] - any */
};
static struct i2c_device_id si5338_id[] = {
{ "si5338", 0 },
{ }
};
static const struct si5338_drv_t drv_configs []={
{"3V3_CMOS_A+", 0x1,0x0,0x17,0x8}, /* bX0 */
{"3V3_CMOS_A-", 0x1,0x0,0x17,0x9}, /* bX1 */
{"3V3_CMOS_B+", 0x2,0x0,0x17,0x4}, /* b0X */
{"3V3_CMOS_B-", 0x2,0x0,0x17,0x6}, /* b1X */
{"3V3_CMOS_A+B+",0x3,0x0,0x17,0x8},
{"3V3_CMOS_A-B+",0x3,0x0,0x17,0x9},
{"3V3_CMOS_A+B-",0x3,0x0,0x17,0x4},
{"3V3_CMOS_A-B-",0x3,0x0,0x17,0x6},
{"2V5_CMOS_A+", 0x1,0x1,0x13,0x8},
{"2V5_CMOS_A-", 0x1,0x1,0x13,0x9},
{"2V5_CMOS_B+", 0x2,0x1,0x13,0x4},
{"2V5_CMOS_B-", 0x2,0x1,0x13,0x6},
{"2V5_CMOS_A+B+",0x3,0x1,0x13,0x8},
{"2V5_CMOS_A-B+",0x3,0x1,0x13,0x9},
{"2V5_CMOS_A+B-",0x3,0x1,0x13,0x4},
{"2V5_CMOS_A-B-",0x3,0x1,0x13,0x6},
{"1V8_CMOS_A+", 0x1,0x2,0x15,0x8},
{"1V8_CMOS_A-", 0x1,0x2,0x15,0x9},
{"1V8_CMOS_B+", 0x2,0x2,0x15,0x4},
{"1V8_CMOS_B-", 0x2,0x2,0x15,0x6},
{"1V8_CMOS_A+B+",0x3,0x2,0x15,0x8},
{"1V8_CMOS_A-B+",0x3,0x2,0x15,0x9},
{"1V8_CMOS_A+B-",0x3,0x2,0x15,0x4},
{"1V8_CMOS_A-B-",0x3,0x2,0x15,0x6},
{"1V5_HSTL_A+", 0x1,0x3,0x1f,0x8},
{"1V5_HSTL_A-", 0x1,0x3,0x1f,0x9},
{"1V5_HSTL_B+", 0x2,0x3,0x1f,0x4},
{"1V5_HSTL_B-", 0x2,0x3,0x1f,0x6},
{"1V5_HSTL_A+B+",0x3,0x3,0x1f,0x8},
{"1V5_HSTL_A-B+",0x3,0x3,0x1f,0x9},
{"1V5_HSTL_A+B-",0x3,0x3,0x1f,0x4},
{"1V5_HSTL_A-B-",0x3,0x3,0x1f,0x6},
{"3V3_SSTL_A+", 0x1,0x0,0x04,0x8},
{"3V3_SSTL_A-", 0x1,0x0,0x04,0x9},
{"3V3_SSTL_B+", 0x2,0x0,0x04,0x4},
{"3V3_SSTL_B-", 0x2,0x0,0x04,0x6},
{"3V3_SSTL_A+B+",0x3,0x0,0x04,0x8},
{"3V3_SSTL_A-B+",0x3,0x0,0x04,0x9},
{"3V3_SSTL_A+B-",0x3,0x0,0x04,0x5},
{"3V3_SSTL_A-B-",0x3,0x0,0x04,0x6},
{"2V5_SSTL_A+", 0x1,0x1,0x0d,0x8},
{"2V5_SSTL_A-", 0x1,0x1,0x0d,0x9},
{"2V5_SSTL_B+", 0x2,0x1,0x0d,0x4},
{"2V5_SSTL_B-", 0x2,0x1,0x0d,0x6},
{"2V5_SSTL_A+B+",0x3,0x1,0x0d,0x8},
{"2V5_SSTL_A-B+",0x3,0x1,0x0d,0x9},
{"2V5_SSTL_A+B-",0x3,0x1,0x0d,0x5},
{"2V5_SSTL_A-B-",0x3,0x1,0x0d,0x6},
{"1V8_SSTL_A+", 0x1,0x2,0x17,0x8},
{"1V8_SSTL_A-", 0x1,0x2,0x17,0x9},
{"1V8_SSTL_B+", 0x2,0x2,0x17,0x4},
{"1V8_SSTL_B-", 0x2,0x2,0x17,0x6},
{"1V8_SSTL_A+B+",0x3,0x2,0x17,0x8},
{"1V8_SSTL_A-B+",0x3,0x2,0x17,0x9},
{"1V8_SSTL_A+B-",0x3,0x2,0x17,0x4},
{"1V8_SSTL_A-B-",0x3,0x2,0x17,0x6},
{"3V3_LVPECL", 0x4,0x0,0x0f,0xc},
{"2V5_LVPECL", 0x4,0x1,0x10,0xc},
{"3V3_LVDS", 0x6,0x0,0x03,0xc},
{"2V5_LVDS", 0x6,0x1,0x04,0xc},
{"1V8_LVDS", 0x6,0x2,0x04,0xc},
{NULL, 0x0,0x0,0x0,0x0},
};
static const char *out_dis_states[]= {"dis_hi-z","dis_low","dis_high","dis_always_on", NULL};
static const char *out_en_states[]= {"output_en","output_dis", NULL};
static const char *out_pwr_states[]= {"output_power_up","output_power_down", NULL};
static const char *ms_pwr_states[]= {"ms_power_up","ms_power_down", NULL};
static const int volatile_registers[]={AWE_STATUS, AWE_SOFT_RESET, AWE_FCAL_07_00, AWE_FCAL_15_08, AWE_FCAL_17_16, -1};
static const char *out_names[]={"output0","output1","output2","output3","outputs", NULL};
static const char *in_freq_names[]={"in_frequency12", "in_frequency3", "in_frequency4", "in_frequency56", "in_frequency12xo", NULL};
static const char *pll_setup_names[]={"pll_freq_fract", "pll_freq_int", "pll_by_out_fract", "pll_by_out_int", NULL};
static const char *out_freq_setup_names[]={
"out0_freq_fract", "out1_freq_fract", "out2_freq_fract", "out3_freq_fract",
"out0_freq_int", "out1_freq_int", "out2_freq_int", "out3_freq_int", NULL};
static u32 awe_msx_ssup[4][3][3]=
{{{AWE_MS0_SSUPP1_07_00,AWE_MS0_SSUPP1_11_08,0},
{AWE_MS0_SSUPP2_07_00,AWE_MS0_SSUPP2_14_08,0},
{AWE_MS0_SSUPP3_07_00,AWE_MS0_SSUPP3_14_08,0}},
{{AWE_MS1_SSUPP1_07_00,AWE_MS1_SSUPP1_11_08,0},
{AWE_MS1_SSUPP2_07_00,AWE_MS1_SSUPP2_14_08,0},
{AWE_MS1_SSUPP3_07_00,AWE_MS1_SSUPP3_14_08,0}},
{{AWE_MS2_SSUPP1_07_00,AWE_MS2_SSUPP1_11_08,0},
{AWE_MS2_SSUPP2_07_00,AWE_MS2_SSUPP2_14_08,0},
{AWE_MS2_SSUPP3_07_00,AWE_MS2_SSUPP3_14_08,0}},
{{AWE_MS3_SSUPP1_07_00,AWE_MS3_SSUPP1_11_08,0},
{AWE_MS3_SSUPP2_07_00,AWE_MS3_SSUPP2_14_08,0},
{AWE_MS3_SSUPP3_07_00,AWE_MS3_SSUPP3_14_08,0}}};
static u32 awe_msx_ssdn[4][3][3]=
{{{AWE_MS0_SSDNP1_07_00,AWE_MS0_SSDNP1_11_08,0},
{AWE_MS0_SSDNP2_07_00,AWE_MS0_SSDNP2_14_08,0},
{AWE_MS0_SSDNP3_07_00,AWE_MS0_SSDNP3_14_08,0}},
{{AWE_MS1_SSDNP1_07_00,AWE_MS1_SSDNP1_11_08,0},
{AWE_MS1_SSDNP2_07_00,AWE_MS1_SSDNP2_14_08,0},
{AWE_MS1_SSDNP3_07_00,AWE_MS1_SSDNP3_14_08,0}},
{{AWE_MS2_SSDNP1_07_00,AWE_MS2_SSDNP1_11_08,0},
{AWE_MS2_SSDNP2_07_00,AWE_MS2_SSDNP2_14_08,0},
{AWE_MS2_SSDNP3_07_00,AWE_MS2_SSDNP3_14_08,0}},
{{AWE_MS3_SSDNP1_07_00,AWE_MS3_SSDNP1_11_08,0},
{AWE_MS3_SSDNP2_07_00,AWE_MS3_SSDNP2_14_08,0},
{AWE_MS3_SSDNP3_07_00,AWE_MS3_SSDNP3_14_08,0}}};
static u32 awe_msx_ssud[4][3]=
{{AWE_MS0_SSUDP1_03_00,AWE_MS0_SSUDP1_11_04,0},
{AWE_MS1_SSUDP1_03_00,AWE_MS1_SSUDP1_11_04,0},
{AWE_MS2_SSUDP1_03_00,AWE_MS2_SSUDP1_11_04,0},
{AWE_MS3_SSUDP1_03_00,AWE_MS3_SSUDP1_11_04,0}};
static const u32 awe_rdiv_in[]= {AWE_R0DIV_IN, AWE_R1DIV_IN, AWE_R2DIV_IN, AWE_R3DIV_IN};
static const u32 awe_rdiv_k[]= {AWE_R0DIV, AWE_R1DIV, AWE_R2DIV, AWE_R3DIV};
static const u32 awe_drv_fmt[]= {AWE_DRV0_FMT, AWE_DRV1_FMT, AWE_DRV2_FMT, AWE_DRV3_FMT};
static const u32 awe_drv_vddo[]= {AWE_DRV0_VDDO, AWE_DRV1_VDDO, AWE_DRV2_VDDO, AWE_DRV3_VDDO};
static const u32 awe_drv_trim[][4]= {{AWE_DRV0_TRIM,0,0}, {AWE_DRV1_TRIM_A,AWE_DRV1_TRIM_B,0},{AWE_DRV2_TRIM,0,0},{AWE_DRV3_TRIM,0,0}};
static const u32 awe_drv_powerdown[]={AWE_DRV0_PDN, AWE_DRV1_PDN, AWE_DRV2_PDN, AWE_DRV3_PDN};
static const u32 awe_drv_disable[]= {AWE_OUT0_DIS, AWE_OUT1_DIS, AWE_OUT2_DIS, AWE_OUT3_DIS, AWE_OUT_ALL_DIS};
static const u32 awe_drv_dis_state[]={AWE_OUT0_DIS_STATE, AWE_OUT1_DIS_STATE, AWE_OUT2_DIS_STATE, AWE_OUT3_DIS_STATE};
static const u32 awe_drv_invert[]= {AWE_DRV0_INV, AWE_DRV1_INV, AWE_DRV2_INV, AWE_DRV3_INV};
static const u32 awe_drv_inv[]= {AWE_DRV0_INV, AWE_DRV1_INV, AWE_DRV2_INV, AWE_DRV3_INV};
static const u32 awe_ms_hs[]= {AWE_MS0_HS, AWE_MS1_HS, AWE_MS2_HS, AWE_MS3_HS};
static const u32 awe_ms_ssmode[]= {AWE_MS0_SSMODE,AWE_MS1_SSMODE,AWE_MS2_SSMODE,AWE_MS3_SSMODE};
/* (register_address << 8) | mask - created from SiLabs output */
static const u32 register_masks[]= {
0x61d,0x1b80,0x1cff,0x1dff,0x1eff,0x1fff,0x20ff,0x21ff,
0x22ff,0x23ff,0x241f,0x251f,0x261f,0x271f,0x28ff,0x297f,
0x2a3f,0x2dff,0x2eff,0x2f3f,0x30ff,0x31ff,0x32ff,0x33ff,
0x34ff,0x35ff,0x36ff,0x37ff,0x38ff,0x39ff,0x3aff,0x3bff,
0x3cff,0x3dff,0x3e3f,0x3fff,0x40ff,0x41ff,0x42ff,0x43ff,
0x44ff,0x45ff,0x46ff,0x47ff,0x48ff,0x493f,0x4aff,0x4bff,
0x4cff,0x4dff,0x4eff,0x4fff,0x50ff,0x51ff,0x52ff,0x53ff,
0x543f,0x55ff,0x56ff,0x57ff,0x58ff,0x59ff,0x5aff,0x5bff,
0x5cff,0x5dff,0x5eff,0x5f3f,0x61ff,0x62ff,0x63ff,0x64ff,
0x65ff,0x66ff,0x67ff,0x68ff,0x69ff,0x6abf,0x6bff,0x6cff,
0x6dff,0x6eff,0x6fff,0x70ff,0x71ff,0x72ff,0x73ff,0x74ff,
0x75ff,0x76ff,0x77ff,0x78ff,0x79ff,0x7aff,0x7bff,0x7cff,
0x7dff,0x7eff,0x7fff,0x80ff,0x810f,0x820f,0x83ff,0x84ff,
0x85ff,0x86ff,0x87ff,0x88ff,0x89ff,0x8aff,0x8bff,0x8cff,
0x8dff,0x8eff,0x8fff,0x90ff,0x98ff,0x99ff,0x9aff,0x9bff,
0x9cff,0x9dff,0x9e0f,0x9f0f,0xa0ff,0xa1ff,0xa2ff,0xa3ff,
0xa4ff,0xa5ff,0xa6ff,0xa7ff,0xa8ff,0xa9ff,0xaaff,0xabff,
0xacff,0xadff,0xaeff,0xafff,0xb0ff,0xb1ff,0xb2ff,0xb3ff,
0xb4ff,0xb50f,0xb6ff,0xb7ff,0xb8ff,0xb9ff,0xbaff,0xbbff,
0xbcff,0xbdff,0xbeff,0xbfff,0xc0ff,0xc1ff,0xc2ff,0xc3ff,
0xc4ff,0xc5ff,0xc6ff,0xc7ff,0xc8ff,0xc9ff,0xcaff,0xcb0f,
0xccff,0xcdff,0xceff,0xcfff,0xd0ff,0xd1ff,0xd2ff,0xd3ff,
0xd4ff,0xd5ff,0xd6ff,0xd7ff,0xd8ff,0xd9ff,0xf202,0x11fff,
0x120ff,0x121ff,0x122ff,0x123ff,0x124ff,0x125ff,0x126ff,0x127ff,
0x128ff,0x129ff,0x12aff,0x12b0f,0x12fff,0x130ff,0x131ff,0x132ff,
0x133ff,0x134ff,0x135ff,0x136ff,0x137ff,0x138ff,0x139ff,0x13aff,
0x13b0f,0x13fff,0x140ff,0x141ff,0x142ff,0x143ff,0x144ff,0x145ff,
0x146ff,0x147ff,0x148ff,0x149ff,0x14aff,0x14b0f,0x14fff,0x150ff,
0x151ff,0x152ff,0x153ff,0x154ff,0x155ff,0x156ff,0x157ff,0x158ff,
0x159ff,0x15aff,0x15b0f};
//AWE_MS0_SSMODE
static const u8 out_div_values[]={1,2,4,8,16,32};
static void si5338_init_of(struct i2c_client *client);
static int get_chn_from_name(const char * name);
static ssize_t invalidate_cache_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t raw_address_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t raw_data_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t raw_hex_address_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_hex_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t raw_hex_data_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_hex_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t raw_hex_all_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_hex_adwe_help_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_hex_adwe_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_hex_adwe_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
//static ssize_t input_xtal_freq_show (struct device *dev, struct device_attribute *attr, char *buf);
//static ssize_t input_xtal_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t input_xtal_freq_txt_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t in_frequency12_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t in_frequency3_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t in_frequency4_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t in_frequency56_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t in_frequency12_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t in_frequency12xo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t in_frequency3_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t in_frequency4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t in_frequency56_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t in_p12_div_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t in_p12_div_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t in_mux_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t in_mux_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t in_mux_txt_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t fb_mux_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t fb_mux_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t fb_mux_txt_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t in_pfd_ref_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t in_pfd_ref_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t in_pfd_ref_txt_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t fb_external_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t fb_external_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t in_pfd_fb_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t in_pfd_fb_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t in_pfd_fb_txt_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t pll_ref_frequency_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t pll_fb_frequency_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t ms_p123_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t ms_p123_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t ms_abc_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t ms_abc_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t ms_pwr_states_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t ms_pwr_states_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static int set_ms_pwr_states(struct device *dev, const char * name, int chn);
static int get_ms_powerup_state(struct device *dev, char * buf, int chn);
static ssize_t ms_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t ss_change_freq_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t ss_change_freq_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t ss_vals_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t ss_vals_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t ss_regs_hex_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t ss_regs_hex_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t pre_init_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t post_init_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t pll_freq_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t pll_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t ms_freq_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t ms_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t out_source_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t out_source_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t out_source_txt_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t out_source_freq_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t out_div_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t out_div_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t out_div_by_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t out_freq_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t out_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t out_pwr_states_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t out_pwr_states_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static int set_out_pwr_states(struct device *dev, const char * name, int chn);
static int get_powerup_state(struct device *dev, char * buf, int chn);
static ssize_t out_en_states_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t out_en_states_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static int set_out_en_states(struct device *dev, const char * name, int chn);
static int get_enabled_state(struct device *dev, char * buf, int chn);
static ssize_t out_dis_states_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t out_dis_states_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static int set_out_dis_states(struct device *dev, const char * name, int chn);
static int get_disabled_state(struct device *dev, char * buf, int chn);
#ifdef GENERATE_EXTRA
static ssize_t drv_powerdown_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t drv_powerdown_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t drv_disable_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t drv_disable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t drv_disabled_state_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t drv_disabled_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t drv_invert_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t drv_invert_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t drv_invert_txt_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t drv_type_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t drv_type_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t drv_type_txt_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t drv_vdd_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t drv_vdd_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t drv_vdd_txt_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t drv_trim_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t drv_auto_trim_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t drv_trim_any_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t drv_txt_show (struct device *dev, struct device_attribute *attr, char *buf);
static int update_drv_trim(struct i2c_client *client, int novtt, int chn); /* no Vtt - CMOS, no termination, where it matters */
static char * get_drv_txt(struct i2c_client *client, int chn);
#endif
static int make_config_out (struct device *dev);
static ssize_t status_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_description_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_route_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_route_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static int get_output_description (struct device *dev, char * buf, int chn);
static int get_out_frequency_txt(struct device *dev, char *buf, int chn);
static ssize_t output_config_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_config_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static int configure_output_driver(struct device *dev, const char * name, int chn);
static int remove_common_factor(u64 * num_denom);
static int _verify_output_channel(struct i2c_client *client,int chn);
static int get_ss_vals(struct device *dev, char * buf, int chn);
static int get_ss_state(struct i2c_client *client, int chn);
static int set_ss_state(struct i2c_client *client, int state, int chn);
static int get_ss_down_rate(struct i2c_client *client, int chn);
static int get_ss_down_amplitude(struct i2c_client *client, int chn);
static int store_ss_down_parameters(struct i2c_client *client, u32 rate, u32 amp, int chn);
static int set_ss_down(struct i2c_client *client, int chn);
static int ss_pre_freq_change(struct i2c_client *client, int chn);
static int ss_post_freq_change(struct i2c_client *client, int chn);
static int calc_ss_down_to_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn);
static int get_ss_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn);
static int set_ss_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn);
static int disable_spread_spectrum(struct i2c_client *client,int chn);
static int enable_spread_spectrum(struct i2c_client *client,int chn);
static int get_drv_powerdown(struct i2c_client *client, int chn);
static int set_drv_powerdown(struct i2c_client *client, int typ, int chn);
static int get_drv_disable(struct i2c_client *client, int chn);
static int set_drv_disable(struct i2c_client *client, int typ, int chn);
static int get_drv_disabled_state(struct i2c_client *client, int chn);
static int set_drv_disabled_state(struct i2c_client *client, int typ, int chn);
static int get_drv_invert(struct i2c_client *client, int chn);
static int set_drv_invert(struct i2c_client *client, int typ, int chn);
static int get_drv_type(struct i2c_client *client, int chn);
static int set_drv_type(struct i2c_client *client, int typ, int chn);
static int get_drv_vdd(struct i2c_client *client, int chn);
static int set_drv_vdd(struct i2c_client *client, int vdd, int chn);
static int get_drv_trim(struct i2c_client *client, int chn);
static int set_drv_trim_any(struct i2c_client *client, int trim, int chn);
static int set_out_div(struct i2c_client *client, int div, int chn); /*chn =0..3 */
static int get_out_div(struct i2c_client *client, int chn); /*chn =0..3 */
static int set_out_div_by_frequency(struct i2c_client *client, u64* out_freq, int chn); /*chn =0..3 */
static int get_out_frequency(struct i2c_client *client, u64* out_freq, int chn); /*chn =0..3 */
static int get_out_source(struct i2c_client *client, int chn);
static int set_out_source(struct i2c_client *client, int chn, int src);
static int get_out_ms(struct i2c_client *client, int chn);
static int get_out_route(struct i2c_client *client, char* buf, int chn);
static int set_out_route(struct i2c_client *client, const char* route, int chn);
static int set_out_frequency_and_route (struct i2c_client *client, u64 *out_freq, int chn, int int_div);
static s64 get_output_src_frequency(struct i2c_client *client, u64 *out_freq, int chn);
static int pre_init(struct i2c_client *client, int clear_all);
static int post_init(struct i2c_client *client, int timeout); /*1 in timeout ~ 0.1ms - i2c read register */
static int reset_ms(struct i2c_client *client, int wait_cycles);
static int get_status(struct i2c_client *client);
static int power_up_down_needed_ms(struct i2c_client *client);
static int disable_output(struct i2c_client *client, int chn);
static int disable_pll_in_fb_mux(struct i2c_client *client); /* to be explicitly enabled if needed */
static int set_pll_paremeters(struct i2c_client *client);
static int is_set_up(struct i2c_client *client);
static int set_misc_registers(struct i2c_client *client);
static int get_ms_powerdown(struct i2c_client *client, int chn);
static int set_ms_powerdown(struct i2c_client *client, int typ, int chn);
static int ms_to_p123(u64* ms,u32 * p123);
static int p123_to_ms(u64* ms,u32 * p123);
static int get_ms_p123(struct i2c_client *client,u32 * p123, int chn); /* chn 0,1,2,3,4 (4 - msn) */
static int set_ms_p123(struct i2c_client *client,u32 * p123, int chn); /* chn 0,1,2,3,4 (4 - msn) */
static int set_pll_freq(struct i2c_client *client, u64 *vco_freq, int int_div);
static int get_pll_freq(struct i2c_client *client,u64 * pll_freq);
static int set_pll_freq_by_out(struct i2c_client *client, u64 *out_freq, int int_msn_div);
static int get_pll_ms_freq(struct i2c_client *client, u64 *out_freq, int chn);
static int set_pll_ms_by_out(struct i2c_client *client, u64 *out_freq, int chn, int int_div);
static s64 get_pll_in_frequency(struct i2c_client *client);
static s64 get_pll_fb_frequency(struct i2c_client *client);
static s64 get_p1div_in_frequency(struct i2c_client *client);
static s64 get_p2div_in_frequency(struct i2c_client *client);
static int set_in_mux(struct i2c_client *client, int data);
static int get_in_mux(struct i2c_client *client);
static int set_fb_mux(struct i2c_client *client, int data);
static int get_fb_mux(struct i2c_client *client);
static int set_in_pdiv(struct i2c_client *client, int div, int chn); /*chn =0,1 */
static int get_in_pdiv(struct i2c_client *client, int chn); /*chn =0,1 */
static int set_in_pfd_ref_fb(struct i2c_client *client, u8 val, int chn); /*chn =0 - ref, 1 - fb*/
static int get_in_pfd_ref_fb(struct i2c_client *client, int chn); /*chn =0,1 */
static int set_fb_external(struct i2c_client *client, u8 val);
static int get_fb_external(struct i2c_client *client);
static int set_in_frequency(struct i2c_client *client, u64 frequency,int src); /* 0 - 12, 1 - 3, 2 - 4, 3 - 5,6, 4 - 12 XO */
static u64 get_in_frequency(struct i2c_client *client,int src);
static s64 read_multireg64 (struct i2c_client *client, const u32 * awe);
static int write_multireg64 (struct i2c_client *client, u64 data, const u32 * awe);
static int read_field (struct i2c_client *client, u32 awe);
static int write_field (struct i2c_client *client, u8 data, u32 awe);
static int write_adwe(struct i2c_client *client, u32 adwe);
static int write_reg(struct i2c_client *client, u16 reg, u8 val, u8 mask);
static int read_reg(struct i2c_client *client, u16 reg);
static void invalidate_cache(struct i2c_client *client);
/* raw access to i2c registers, need to set address (9 bits) first, then r/w data */
static DEVICE_ATTR(invalidate_cache, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, invalidate_cache_store);
static DEVICE_ATTR(address, SYSFS_PERMISSIONS, raw_address_show, raw_address_store);
static DEVICE_ATTR(data, SYSFS_PERMISSIONS, raw_data_show, raw_data_store);
static DEVICE_ATTR(hex_address, SYSFS_PERMISSIONS, raw_hex_address_show,raw_hex_address_store);
static DEVICE_ATTR(hex_data, SYSFS_PERMISSIONS, raw_hex_data_show, raw_hex_data_store);
static DEVICE_ATTR(hex_all, SYSFS_PERMISSIONS & SYSFS_READONLY, raw_hex_all_show, NULL);
static DEVICE_ATTR(hex_adwe, SYSFS_PERMISSIONS, raw_hex_adwe_show, raw_hex_adwe_store);
static DEVICE_ATTR(hex_adwe_help, SYSFS_PERMISSIONS & SYSFS_READONLY, raw_hex_adwe_help_show, NULL);
static struct attribute *raw_dev_attrs[] = {
&dev_attr_invalidate_cache.attr,
&dev_attr_address.attr,
&dev_attr_data.attr,
&dev_attr_hex_address.attr,
&dev_attr_hex_data.attr,
&dev_attr_hex_all.attr,
&dev_attr_hex_adwe.attr,
&dev_attr_hex_adwe_help.attr,
NULL
};
static const struct attribute_group dev_attr_raw_group = {
.attrs = raw_dev_attrs,
.name = "raw",
};
//static DEVICE_ATTR(xtal_freq, SYSFS_PERMISSIONS, input_xtal_freq_show, input_xtal_freq_store);
static DEVICE_ATTR(xtal_freq_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, input_xtal_freq_txt_show, NULL);
static DEVICE_ATTR(in_frequency12, SYSFS_PERMISSIONS, in_frequency12_show, in_frequency12_store);
static DEVICE_ATTR(in_frequency12xo, SYSFS_PERMISSIONS, in_frequency12_show, in_frequency12xo_store);
static DEVICE_ATTR(in_frequency3, SYSFS_PERMISSIONS, in_frequency3_show, in_frequency3_store);
static DEVICE_ATTR(in_frequency4, SYSFS_PERMISSIONS, in_frequency4_show, in_frequency4_store);
static DEVICE_ATTR(in_frequency56, SYSFS_PERMISSIONS, in_frequency56_show, in_frequency56_store);
static DEVICE_ATTR(in_p1_div, SYSFS_PERMISSIONS, in_p12_div_show, in_p12_div_store);
static DEVICE_ATTR(in_p2_div, SYSFS_PERMISSIONS, in_p12_div_show, in_p12_div_store);
static DEVICE_ATTR(in_mux, SYSFS_PERMISSIONS, in_mux_show, in_mux_store);
static DEVICE_ATTR(in_mux_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, in_mux_txt_show, NULL);
static DEVICE_ATTR(fb_mux, SYSFS_PERMISSIONS, fb_mux_show, fb_mux_store);
static DEVICE_ATTR(fb_mux_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, fb_mux_txt_show, NULL);
static DEVICE_ATTR(in_pfd_ref, SYSFS_PERMISSIONS, in_pfd_ref_show, in_pfd_ref_store);
static DEVICE_ATTR(in_pfd_ref_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, in_pfd_ref_txt_show, NULL);
static DEVICE_ATTR(in_pfd_fb, SYSFS_PERMISSIONS, in_pfd_fb_show, in_pfd_fb_store);
static DEVICE_ATTR(in_pfd_fb_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, in_pfd_fb_txt_show, NULL);
static DEVICE_ATTR(pll_ref_frequency,SYSFS_PERMISSIONS & SYSFS_READONLY, pll_ref_frequency_show, NULL);
static DEVICE_ATTR(pll_fb_frequency, SYSFS_PERMISSIONS & SYSFS_READONLY, pll_fb_frequency_show, NULL);
static DEVICE_ATTR(fb_external, SYSFS_PERMISSIONS, fb_external_show, fb_external_store);
static struct attribute *input_dev_attrs[] = {
// &dev_attr_xtal_freq.attr,
&dev_attr_xtal_freq_txt.attr,
&dev_attr_in_frequency12.attr,
&dev_attr_in_frequency12xo.attr,
&dev_attr_in_frequency3.attr,
&dev_attr_in_frequency4.attr,
&dev_attr_in_frequency56.attr,
&dev_attr_in_p1_div.attr,
&dev_attr_in_p2_div.attr,
&dev_attr_in_mux.attr,
&dev_attr_in_mux_txt.attr,
&dev_attr_fb_mux.attr,
&dev_attr_fb_mux_txt.attr,
&dev_attr_in_pfd_ref.attr,
&dev_attr_in_pfd_ref_txt.attr,
&dev_attr_in_pfd_fb.attr,
&dev_attr_in_pfd_fb_txt.attr,
&dev_attr_pll_ref_frequency.attr,
&dev_attr_pll_fb_frequency.attr,
&dev_attr_fb_external.attr,
NULL
};
static const struct attribute_group dev_attr_input_group = {
.attrs = input_dev_attrs,
.name = "input",
};
/* has to have/not have '_fract' in the name */
static DEVICE_ATTR(ms0_freq_fract,SYSFS_PERMISSIONS, ms_freq_show, ms_freq_store);
static DEVICE_ATTR(ms0_freq_int, SYSFS_PERMISSIONS, ms_freq_show, ms_freq_store);
static DEVICE_ATTR(ms1_freq_fract,SYSFS_PERMISSIONS, ms_freq_show, ms_freq_store);
static DEVICE_ATTR(ms1_freq_int, SYSFS_PERMISSIONS, ms_freq_show, ms_freq_store);
static DEVICE_ATTR(ms2_freq_fract,SYSFS_PERMISSIONS, ms_freq_show, ms_freq_store);
static DEVICE_ATTR(ms2_freq_int, SYSFS_PERMISSIONS, ms_freq_show, ms_freq_store);
static DEVICE_ATTR(ms3_freq_fract,SYSFS_PERMISSIONS, ms_freq_show, ms_freq_store);
static DEVICE_ATTR(ms3_freq_int, SYSFS_PERMISSIONS, ms_freq_show, ms_freq_store);
static DEVICE_ATTR(ms0_p123, SYSFS_PERMISSIONS, ms_p123_show, ms_p123_store);
static DEVICE_ATTR(ms0_abc, SYSFS_PERMISSIONS, ms_abc_show, ms_abc_store);
static DEVICE_ATTR(ms1_p123, SYSFS_PERMISSIONS, ms_p123_show, ms_p123_store);
static DEVICE_ATTR(ms1_abc, SYSFS_PERMISSIONS, ms_abc_show, ms_abc_store);
static DEVICE_ATTR(ms2_p123, SYSFS_PERMISSIONS, ms_p123_show, ms_p123_store);
static DEVICE_ATTR(ms2_abc, SYSFS_PERMISSIONS, ms_abc_show, ms_abc_store);
static DEVICE_ATTR(ms3_p123, SYSFS_PERMISSIONS, ms_p123_show, ms_p123_store);
static DEVICE_ATTR(ms3_abc, SYSFS_PERMISSIONS, ms_abc_show, ms_abc_store);
static DEVICE_ATTR(msn_p123, SYSFS_PERMISSIONS, ms_p123_show, ms_p123_store);
static DEVICE_ATTR(msn_abc, SYSFS_PERMISSIONS, ms_abc_show, ms_abc_store);
static DEVICE_ATTR(ms_power_down, SYSFS_PERMISSIONS, ms_pwr_states_show, ms_pwr_states_store);
static DEVICE_ATTR(ms_power_up, SYSFS_PERMISSIONS, ms_pwr_states_show, ms_pwr_states_store);
static DEVICE_ATTR(ms_reset, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, ms_reset_store);
static struct attribute *multisynth_attrs[] = {
&dev_attr_ms0_freq_fract.attr,
&dev_attr_ms0_freq_int.attr,
&dev_attr_ms1_freq_fract.attr,
&dev_attr_ms1_freq_int.attr,
&dev_attr_ms2_freq_fract.attr,
&dev_attr_ms2_freq_int.attr,
&dev_attr_ms3_freq_fract.attr,
&dev_attr_ms3_freq_int.attr,
&dev_attr_ms0_p123.attr,
&dev_attr_ms0_abc.attr,
&dev_attr_ms1_p123.attr,
&dev_attr_ms1_abc.attr,
&dev_attr_ms2_p123.attr,
&dev_attr_ms2_abc.attr,
&dev_attr_ms3_p123.attr,
&dev_attr_ms3_abc.attr,
&dev_attr_msn_p123.attr,
&dev_attr_msn_abc.attr,
&dev_attr_ms_power_down.attr,
&dev_attr_ms_power_up.attr,
&dev_attr_ms_reset.attr,
NULL
};
static const struct attribute_group dev_attr_multisynth_group = {
.attrs = multisynth_attrs,
.name = "multiSynth",
};
/* Spread spectrum group */
static DEVICE_ATTR(ss_change_freq_mode, SYSFS_PERMISSIONS, ss_change_freq_mode_show, ss_change_freq_mode_store);
static DEVICE_ATTR(ss0_values, SYSFS_PERMISSIONS, ss_vals_show, ss_vals_store);
static DEVICE_ATTR(ss1_values, SYSFS_PERMISSIONS, ss_vals_show, ss_vals_store);
static DEVICE_ATTR(ss2_values, SYSFS_PERMISSIONS, ss_vals_show, ss_vals_store);
static DEVICE_ATTR(ss3_values, SYSFS_PERMISSIONS, ss_vals_show, ss_vals_store);
static DEVICE_ATTR(ss0_regs_hex, SYSFS_PERMISSIONS, ss_regs_hex_show, ss_regs_hex_store);
static DEVICE_ATTR(ss1_regs_hex, SYSFS_PERMISSIONS, ss_regs_hex_show, ss_regs_hex_store);
static DEVICE_ATTR(ss2_regs_hex, SYSFS_PERMISSIONS, ss_regs_hex_show, ss_regs_hex_store);
static DEVICE_ATTR(ss3_regs_hex, SYSFS_PERMISSIONS, ss_regs_hex_show, ss_regs_hex_store);
static struct attribute *spread_spectrum_attrs[] = {
&dev_attr_ss_change_freq_mode.attr,
&dev_attr_ss0_values.attr,
&dev_attr_ss1_values.attr,
&dev_attr_ss2_values.attr,
&dev_attr_ss3_values.attr,
&dev_attr_ss0_regs_hex.attr,
&dev_attr_ss1_regs_hex.attr,
&dev_attr_ss2_regs_hex.attr,
&dev_attr_ss3_regs_hex.attr,
&dev_attr_ms_reset.attr,
NULL
};
static const struct attribute_group dev_attr_spread_spectrum_group = {
.attrs = spread_spectrum_attrs,
.name = "spread_spectrum",
};
static DEVICE_ATTR(pre_init, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, pre_init_store);
static DEVICE_ATTR(pre_init_clear, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, pre_init_store);
static DEVICE_ATTR(post_init, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, post_init_store);
static DEVICE_ATTR(pll_freq_fract, SYSFS_PERMISSIONS, pll_freq_show, pll_freq_store);
static DEVICE_ATTR(pll_freq_int, SYSFS_PERMISSIONS, pll_freq_show, pll_freq_store);
static DEVICE_ATTR(pll_by_out_fract,SYSFS_PERMISSIONS, pll_freq_show, pll_freq_store);
static DEVICE_ATTR(pll_by_out_int, SYSFS_PERMISSIONS, pll_freq_show, pll_freq_store);
static struct attribute *pll_dev_attrs[] = {
/* &dev_attr_pre_init.attr,
&dev_attr_pre_init_clear.attr,
&dev_attr_post_init.attr, */
&dev_attr_pll_ref_frequency.attr,
&dev_attr_pll_freq_fract.attr,
&dev_attr_pll_freq_int.attr,
&dev_attr_pll_by_out_fract.attr,
&dev_attr_pll_by_out_int.attr,
NULL
};
static const struct attribute_group dev_attr_pll_group = {
.attrs = pll_dev_attrs,
.name = "pll",
};
static DEVICE_ATTR(out0_source, SYSFS_PERMISSIONS, out_source_show, out_source_store);
static DEVICE_ATTR(out0_source_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, out_source_txt_show, NULL);
static DEVICE_ATTR(out1_source, SYSFS_PERMISSIONS, out_source_show, out_source_store);
static DEVICE_ATTR(out1_source_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, out_source_txt_show, NULL);
static DEVICE_ATTR(out2_source, SYSFS_PERMISSIONS, out_source_show, out_source_store);
static DEVICE_ATTR(out2_source_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, out_source_txt_show, NULL);
static DEVICE_ATTR(out3_source, SYSFS_PERMISSIONS, out_source_show, out_source_store);
static DEVICE_ATTR(out3_source_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, out_source_txt_show, NULL);
static DEVICE_ATTR(out0_source_freq,SYSFS_PERMISSIONS & SYSFS_READONLY, out_source_freq_show, NULL);
static DEVICE_ATTR(out1_source_freq,SYSFS_PERMISSIONS & SYSFS_READONLY, out_source_freq_show, NULL);
static DEVICE_ATTR(out2_source_freq,SYSFS_PERMISSIONS & SYSFS_READONLY, out_source_freq_show, NULL);
static DEVICE_ATTR(out3_source_freq,SYSFS_PERMISSIONS & SYSFS_READONLY, out_source_freq_show, NULL);
static DEVICE_ATTR(out0_div, SYSFS_PERMISSIONS, out_div_show, out_div_store);
static DEVICE_ATTR(out1_div, SYSFS_PERMISSIONS, out_div_show, out_div_store);
static DEVICE_ATTR(out2_div, SYSFS_PERMISSIONS, out_div_show, out_div_store);
static DEVICE_ATTR(out3_div, SYSFS_PERMISSIONS, out_div_show, out_div_store);
static DEVICE_ATTR(out0_div_by_freq,SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, out_div_by_freq_store);
static DEVICE_ATTR(out0_freq_int, SYSFS_PERMISSIONS, out_freq_show, out_freq_store);
static DEVICE_ATTR(out0_freq_fract, SYSFS_PERMISSIONS, out_freq_show, out_freq_store);
static DEVICE_ATTR(out1_div_by_freq,SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, out_div_by_freq_store);
static DEVICE_ATTR(out1_freq_int, SYSFS_PERMISSIONS, out_freq_show, out_freq_store);
static DEVICE_ATTR(out1_freq_fract, SYSFS_PERMISSIONS, out_freq_show, out_freq_store);
static DEVICE_ATTR(out2_div_by_freq,SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, out_div_by_freq_store);
static DEVICE_ATTR(out2_freq_int, SYSFS_PERMISSIONS, out_freq_show, out_freq_store);
static DEVICE_ATTR(out2_freq_fract, SYSFS_PERMISSIONS, out_freq_show, out_freq_store);
static DEVICE_ATTR(out3_div_by_freq,SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, out_div_by_freq_store);
static DEVICE_ATTR(out3_freq_int, SYSFS_PERMISSIONS, out_freq_show, out_freq_store);
static DEVICE_ATTR(out3_freq_fract, SYSFS_PERMISSIONS, out_freq_show, out_freq_store);
static DEVICE_ATTR(out0_route, SYSFS_PERMISSIONS, output_route_show, output_route_store);
static DEVICE_ATTR(out1_route, SYSFS_PERMISSIONS, output_route_show, output_route_store);
static DEVICE_ATTR(out2_route, SYSFS_PERMISSIONS, output_route_show, output_route_store);
static DEVICE_ATTR(out3_route, SYSFS_PERMISSIONS, output_route_show, output_route_store);
//output_route_show
static struct attribute *output_dev_attrs[] = {
&dev_attr_out0_source.attr,
&dev_attr_out0_source_txt.attr,
&dev_attr_out1_source.attr,
&dev_attr_out1_source_txt.attr,
&dev_attr_out2_source.attr,
&dev_attr_out2_source_txt.attr,
&dev_attr_out3_source.attr,
&dev_attr_out3_source_txt.attr,
&dev_attr_out0_source_freq.attr,
&dev_attr_out1_source_freq.attr,
&dev_attr_out2_source_freq.attr,
&dev_attr_out3_source_freq.attr,
&dev_attr_out0_div.attr,
&dev_attr_out1_div.attr,
&dev_attr_out2_div.attr,
&dev_attr_out3_div.attr,
&dev_attr_out0_div_by_freq.attr,
&dev_attr_out1_div_by_freq.attr,
&dev_attr_out2_div_by_freq.attr,
&dev_attr_out3_div_by_freq.attr,
&dev_attr_out0_freq_int.attr,
&dev_attr_out1_freq_int.attr,
&dev_attr_out2_freq_int.attr,
&dev_attr_out3_freq_int.attr,
&dev_attr_out0_freq_fract.attr,
&dev_attr_out1_freq_fract.attr,
&dev_attr_out2_freq_fract.attr,
&dev_attr_out3_freq_fract.attr,
&dev_attr_out0_route.attr,
&dev_attr_out1_route.attr,
&dev_attr_out2_route.attr,
&dev_attr_out3_route.attr,
NULL
};
static const struct attribute_group dev_attr_output_group = {
.attrs = output_dev_attrs,
.name = "output_clocks",
};
/* output drivers */
/* NOTE: state of the outputs changes with clock only, changing "dis_low" to "dis_high" does not work when disabled.
* Going through "dis_always_on" works
*/
#ifdef GENERATE_EXTRA
static DEVICE_ATTR(drv0_powerdown, SYSFS_PERMISSIONS, drv_powerdown_show, drv_powerdown_store);
static DEVICE_ATTR(drv1_powerdown, SYSFS_PERMISSIONS, drv_powerdown_show, drv_powerdown_store);
static DEVICE_ATTR(drv2_powerdown, SYSFS_PERMISSIONS, drv_powerdown_show, drv_powerdown_store);
static DEVICE_ATTR(drv3_powerdown, SYSFS_PERMISSIONS, drv_powerdown_show, drv_powerdown_store);
static DEVICE_ATTR(drv0_disable, SYSFS_PERMISSIONS, drv_disable_show, drv_disable_store);
static DEVICE_ATTR(drv1_disable, SYSFS_PERMISSIONS, drv_disable_show, drv_disable_store);
static DEVICE_ATTR(drv2_disable, SYSFS_PERMISSIONS, drv_disable_show, drv_disable_store);
static DEVICE_ATTR(drv3_disable, SYSFS_PERMISSIONS, drv_disable_show, drv_disable_store);
static DEVICE_ATTR(drv0_disabled_state,SYSFS_PERMISSIONS, drv_disabled_state_show, drv_disabled_state_store);
static DEVICE_ATTR(drv1_disabled_state,SYSFS_PERMISSIONS, drv_disabled_state_show, drv_disabled_state_store);
static DEVICE_ATTR(drv2_disabled_state,SYSFS_PERMISSIONS, drv_disabled_state_show, drv_disabled_state_store);
static DEVICE_ATTR(drv3_disabled_state,SYSFS_PERMISSIONS, drv_disabled_state_show, drv_disabled_state_store);
static DEVICE_ATTR(drv0_invert, SYSFS_PERMISSIONS, drv_invert_show, drv_invert_store);
static DEVICE_ATTR(drv1_invert, SYSFS_PERMISSIONS, drv_invert_show, drv_invert_store);
static DEVICE_ATTR(drv2_invert, SYSFS_PERMISSIONS, drv_invert_show, drv_invert_store);
static DEVICE_ATTR(drv3_invert, SYSFS_PERMISSIONS, drv_invert_show, drv_invert_store);
static DEVICE_ATTR(drv0_invert_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_invert_txt_show, NULL);
static DEVICE_ATTR(drv1_invert_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_invert_txt_show, NULL);
static DEVICE_ATTR(drv2_invert_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_invert_txt_show, NULL);
static DEVICE_ATTR(drv3_invert_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_invert_txt_show, NULL);
static DEVICE_ATTR(drv0_type, SYSFS_PERMISSIONS, drv_type_show, drv_type_store);
static DEVICE_ATTR(drv1_type, SYSFS_PERMISSIONS, drv_type_show, drv_type_store);
static DEVICE_ATTR(drv2_type, SYSFS_PERMISSIONS, drv_type_show, drv_type_store);
static DEVICE_ATTR(drv3_type, SYSFS_PERMISSIONS, drv_type_show, drv_type_store);
static DEVICE_ATTR(drv0_type_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_type_txt_show, NULL);
static DEVICE_ATTR(drv1_type_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_type_txt_show, NULL);
static DEVICE_ATTR(drv2_type_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_type_txt_show, NULL);
static DEVICE_ATTR(drv3_type_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_type_txt_show, NULL);
static DEVICE_ATTR(drv0_vdd, SYSFS_PERMISSIONS, drv_vdd_show, drv_vdd_store);
static DEVICE_ATTR(drv1_vdd, SYSFS_PERMISSIONS, drv_vdd_show, drv_vdd_store);
static DEVICE_ATTR(drv2_vdd, SYSFS_PERMISSIONS, drv_vdd_show, drv_vdd_store);
static DEVICE_ATTR(drv3_vdd, SYSFS_PERMISSIONS, drv_vdd_show, drv_vdd_store);
static DEVICE_ATTR(drv0_vdd_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_vdd_txt_show, NULL);
static DEVICE_ATTR(drv1_vdd_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_vdd_txt_show, NULL);
static DEVICE_ATTR(drv2_vdd_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_vdd_txt_show, NULL);
static DEVICE_ATTR(drv3_vdd_txt, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_vdd_txt_show, NULL);
static DEVICE_ATTR(drv0_trim, SYSFS_PERMISSIONS, drv_trim_show, drv_trim_any_store);
static DEVICE_ATTR(drv1_trim, SYSFS_PERMISSIONS, drv_trim_show, drv_trim_any_store);
static DEVICE_ATTR(drv2_trim, SYSFS_PERMISSIONS, drv_trim_show, drv_trim_any_store);
static DEVICE_ATTR(drv3_trim, SYSFS_PERMISSIONS, drv_trim_show, drv_trim_any_store);
static DEVICE_ATTR(drv0_auto_trim, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, drv_auto_trim_store);
static DEVICE_ATTR(drv1_auto_trim, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, drv_auto_trim_store);
static DEVICE_ATTR(drv2_auto_trim, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, drv_auto_trim_store);
static DEVICE_ATTR(drv3_auto_trim, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, drv_auto_trim_store);
static DEVICE_ATTR(drv0_description, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_txt_show, NULL);
static DEVICE_ATTR(drv1_description, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_txt_show, NULL);
static DEVICE_ATTR(drv2_description, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_txt_show, NULL);
static DEVICE_ATTR(drv3_description, SYSFS_PERMISSIONS & SYSFS_READONLY, drv_txt_show, NULL);
static struct attribute *output_extra_dev_attrs[] = {
&dev_attr_drv0_powerdown.attr,
&dev_attr_drv1_powerdown.attr,
&dev_attr_drv2_powerdown.attr,
&dev_attr_drv3_powerdown.attr,
&dev_attr_drv0_disable.attr,
&dev_attr_drv1_disable.attr,
&dev_attr_drv2_disable.attr,
&dev_attr_drv3_disable.attr,
&dev_attr_drv0_disabled_state.attr,
&dev_attr_drv1_disabled_state.attr,
&dev_attr_drv2_disabled_state.attr,
&dev_attr_drv3_disabled_state.attr,
&dev_attr_drv0_invert.attr,
&dev_attr_drv1_invert.attr,
&dev_attr_drv2_invert.attr,
&dev_attr_drv3_invert.attr,
&dev_attr_drv0_invert_txt.attr,
&dev_attr_drv1_invert_txt.attr,
&dev_attr_drv2_invert_txt.attr,
&dev_attr_drv3_invert_txt.attr,
&dev_attr_drv0_type.attr,
&dev_attr_drv1_type.attr,
&dev_attr_drv2_type.attr,
&dev_attr_drv3_type.attr,
&dev_attr_drv0_type_txt.attr,
&dev_attr_drv1_type_txt.attr,
&dev_attr_drv2_type_txt.attr,
&dev_attr_drv3_type_txt.attr,
&dev_attr_drv0_vdd.attr,
&dev_attr_drv1_vdd.attr,
&dev_attr_drv2_vdd.attr,
&dev_attr_drv3_vdd.attr,
&dev_attr_drv0_vdd_txt.attr,
&dev_attr_drv1_vdd_txt.attr,
&dev_attr_drv2_vdd_txt.attr,
&dev_attr_drv3_vdd_txt.attr,
&dev_attr_drv0_trim.attr,
&dev_attr_drv1_trim.attr,
&dev_attr_drv2_trim.attr,
&dev_attr_drv3_trim.attr,
&dev_attr_drv0_auto_trim.attr,
&dev_attr_drv1_auto_trim.attr,
&dev_attr_drv2_auto_trim.attr,
&dev_attr_drv3_auto_trim.attr,
&dev_attr_drv0_description.attr,
&dev_attr_drv1_description.attr,
&dev_attr_drv2_description.attr,
&dev_attr_drv3_description.attr,
NULL
};
static const struct attribute_group dev_attr_output_extra_group = {
.attrs = output_extra_dev_attrs,
.name = "output_extra",
};
#endif
/* root directory */
static DEVICE_ATTR(outputs, SYSFS_PERMISSIONS & SYSFS_READONLY, output_description_show, NULL);
static DEVICE_ATTR(status, SYSFS_PERMISSIONS & SYSFS_READONLY, status_show, NULL);
static struct attribute *root_dev_attrs[] = {
&dev_attr_pre_init.attr,
&dev_attr_pre_init_clear.attr,
&dev_attr_post_init.attr,
&dev_attr_outputs.attr,
&dev_attr_status.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static int get_chn_from_name(const char * name)
{
char * cp = strpbrk(name,"0123456789");
return (cp)?(cp[0]-'0'):-1;
}
static int make_config_out(struct device *dev)
{
int retval=-1;
int index,iout,num_types,num_files;
struct attribute **pattrs; /* array of pointers to attibutes */
struct device_attribute *dev_attrs;
struct attribute_group *attr_group;
for (num_types=0;drv_configs[num_types].description;num_types++);
num_files=num_types;
for (iout=0;out_dis_states[iout];iout++) num_files++;
for (iout=0;out_en_states[iout];iout++) num_files++;
for (iout=0;out_pwr_states[iout];iout++) num_files++;
for (iout=0;out_names[iout];iout++) num_files++;
pattrs = devm_kzalloc(dev,(num_files+1)*sizeof(pattrs[0]), GFP_KERNEL);
if (!pattrs) return -ENOMEM;
dev_attrs = devm_kzalloc(dev, num_files*sizeof(dev_attrs[0]), GFP_KERNEL);
if (!dev_attrs) return -ENOMEM;
attr_group = devm_kzalloc(dev, sizeof(*attr_group), GFP_KERNEL);
if (!attr_group) return -ENOMEM;
memset(dev_attrs, 0, num_files*sizeof(dev_attrs[0]));
memset(attr_group, 0, sizeof(*attr_group));
for (index=0;indexname = "output_drivers";
attr_group->attrs =pattrs;
dev_dbg(dev,"name=%s, &dev->kobj=0x%08x\n",attr_group->name, (int) (&dev->kobj));
index=0;
while ((*attr_group).attrs[index]){
dev_dbg(dev,"attr=%s\n",attr_group->attrs[index]->name);
index++;
}
if (&dev->kobj) {
retval = sysfs_create_group(&dev->kobj, attr_group);
}
return retval;
}
static ssize_t status_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int status;
struct i2c_client *client = to_i2c_client(dev);
if (((status=get_status(client)))<0) return status;
return sprintf(buf,"0x%x input clock: %s, feedback clock: %s, PLL lock: %s, calibration: %s\n",
status,(status & 0x4)?"LOST":"OK",(status & 0x8)?"LOST":"OK",(status & 0x10)?"LOST":"OK",(status & 0x10)?"IN PROGRESS":"DONE");
}
static ssize_t output_description_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int i,i1,rc,len=0,show_number,ms;
struct i2c_client *client = to_i2c_client(dev);
for (i=0; out_names[i]; i++) if (strcmp(attr->attr.name,out_names[i]) == 0) break;
if (!out_names[i]) return -EINVAL;
if (i==4) { /* all outputs */
i=0;
i1=4;
show_number=1;
} else {
i1=i+1;
show_number=0;
}
for (;i= 0){
rc=sprintf(buf,", ");
buf+=rc;
len+=rc;
if (((rc=get_ms_powerup_state(dev, buf,i)))<0) return rc;
buf+=rc;
len+=rc;
}
rc=sprintf(buf,", disabled state: ");
buf+=rc;
len+=rc;
if (((rc=get_disabled_state(dev, buf,i)))<0) return rc;
buf+=rc;
len+=rc;
rc=sprintf(buf,", ");
buf+=rc;
len+=rc;
if (((rc=get_powerup_state(dev, buf,i)))<0) return rc;
buf+=rc;
len+=rc;
rc=sprintf(buf,", ");
buf+=rc;
len+=rc;
if (((rc=get_enabled_state(dev, buf,i)))<0) return rc;
buf+=rc;
len+=rc;
/* show spread spectum settings */
rc=sprintf(buf,", ");
buf+=rc;
len+=rc;
if (((rc=get_ss_vals(dev, buf, i)))<0) return rc;
buf+=rc;
len+=rc;
rc=sprintf(buf,"\n");
buf+=rc;
len+=rc;
}
return len;
}
static ssize_t output_route_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn,rc,len=0;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name);
if (((rc=get_out_route(client, buf,chn)))<0) return rc;
buf+=rc;
len+=rc;
rc=sprintf(buf,"\n");
buf+=rc;
len+=rc;
return len;
}
static ssize_t output_route_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, rc;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name);
if (((rc=set_out_route(client, buf, chn)))<0) return rc;
return count;
}
//static void invalidate_cache(struct i2c_client *client)
static int get_output_description (struct device *dev, char * buf, int chn)
{
int drv_type, drv_vdd, drv_trim, drv_invert,i;
struct i2c_client *client = to_i2c_client(dev);
if (((i=_verify_output_channel(client,chn)))<0) return i;
if (((drv_type= get_drv_type (client, chn)))<0) return drv_type;
if (((drv_vdd= get_drv_vdd (client, chn)))<0) return drv_vdd;
if (((drv_trim= get_drv_trim (client, chn)))<0) return drv_trim;
if (((drv_invert=get_drv_invert (client, chn)))<0) return drv_invert;
for (i=0; drv_configs[i].description; i++) {
if ((drv_configs[i].fmt==drv_type) &&
(drv_configs[i].vdd==drv_vdd) &&
(drv_configs[i].trim==drv_trim) &&
((drv_invert |(drv_configs[i].invert>>2)) == ((drv_configs[i].invert & 3) | (drv_configs[i].invert>>2)))){
return sprintf (buf,drv_configs[i].description);
}
}
return sprintf (buf,"Invalid output configuration: type = %d, vdd=%d, trim=%d, invert=%d",drv_type,drv_vdd,drv_trim,drv_invert);
}
static int get_out_frequency_txt(struct device *dev, char *buf, int chn)
{
int rc;
u64 out_freq[3];
struct i2c_client *client = to_i2c_client(dev);
if (((rc=get_out_frequency(client, out_freq, chn)))<0) return sprintf (buf,"Not set");
if (out_freq[1]==0) return sprintf(buf, "%lld Hz",out_freq[0]);
return sprintf(buf, "%lld-%lld/%lld Hz",out_freq[0],out_freq[1],out_freq[2]);
}
static ssize_t output_config_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn, i, rc;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
for (i=0; drv_configs[i].description; i++) if (strcmp(attr->attr.name,drv_configs[i].description) == 0) {
break;
}
if (!drv_configs[i].description) return -EINVAL; /* filename does not exist - BUG */
for (chn=0;chn<4;chn++){
if (((rc=get_drv_type (client, chn)))<0) return rc;
if (rc!=drv_configs[i].fmt) continue;
if (((rc=get_drv_vdd (client, chn)))<0) return rc;
if (rc!=drv_configs[i].vdd) continue;
if (((rc=get_drv_trim (client, chn)))<0) return rc;
if (rc!=drv_configs[i].trim) continue;
if (((rc=get_drv_invert (client, chn)))<0) return rc;
if (rc!= (drv_configs[i].invert & 3)) continue;
buf+=sprintf(buf," %d",chn);
}
buf+=sprintf(buf,"\n");
return buf-cp;
}
static ssize_t output_config_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, num_bytes,rc;
while ((rc=sscanf(buf, "%d%n", &chn,&num_bytes))){
dev_dbg(dev,"buf=%s rc==%d chn=%d num_bytes=%d", buf, rc,chn,num_bytes);
buf+=num_bytes;
if (((rc=configure_output_driver(dev, attr->attr.name, chn)))<0) return rc;
}
return count;
}
static int configure_output_driver(struct device *dev, const char * name, int chn)
{
int i,rc;
struct i2c_client *client = to_i2c_client(dev);
dev_dbg(dev,"name=%s chn=%d", name,chn);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
for (i=0; drv_configs[i].description; i++) if (strcmp(name,drv_configs[i].description) == 0) {
if (((rc=set_drv_type (client, drv_configs[i].fmt, chn)))<0) return rc;
if (((rc=set_drv_vdd (client, drv_configs[i].vdd, chn)))<0) return rc;
if (((rc=set_drv_trim_any(client, drv_configs[i].trim, chn)))<0) return rc;
if (((rc=set_drv_invert (client, drv_configs[i].invert&3, chn)))<0) return rc;
return 0;
}
return -EINVAL;
}
static int si5338_sysfs_register(struct device *dev)
{
int retval=0;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_raw_group)))<0) return retval;
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_input_group)))<0) return retval;
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_multisynth_group)))<0) return retval;
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_pll_group)))<0) return retval;
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_output_group)))<0) return retval;
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_spread_spectrum_group)))<0) return retval;
#ifdef GENERATE_EXTRA
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_output_extra_group)))<0) return retval;
#endif
if (((retval = make_config_out (dev)))<0) return retval;
}
return retval;
}
static ssize_t invalidate_cache_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
invalidate_cache(client);
return count;
}
static ssize_t raw_address_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct si5338_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
return sprintf(buf, "%d\n",clientdata->reg_addr);
}
static ssize_t raw_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct si5338_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
sscanf(buf, "%du", &clientdata->reg_addr);
return count;
}
static ssize_t raw_data_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct si5338_data_t *clientdata= i2c_get_clientdata(client);
int data= read_reg(client, clientdata->reg_addr);
return sprintf(buf, "%d\n",data);
}
static ssize_t raw_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct si5338_data_t *clientdata= i2c_get_clientdata(client);
int data;
sscanf(buf, "%du", &data);
write_reg(client, clientdata->reg_addr, data, 0xff); /* write all register, it is up to user to do R-mod-W */
return count;
}
static ssize_t raw_hex_address_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct si5338_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
return sprintf(buf, "0x%03x\n",clientdata->reg_addr);
}
static ssize_t raw_hex_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct si5338_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
sscanf(buf, "%x", &clientdata->reg_addr);
return count;
}
static ssize_t raw_hex_data_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct si5338_data_t *clientdata= i2c_get_clientdata(client);
int data= read_reg(client, clientdata->reg_addr);
return sprintf(buf, "0x%02x\n",data);
}
static ssize_t raw_hex_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct si5338_data_t *clientdata= i2c_get_clientdata(client);
int data;
sscanf(buf, "%x", &data);
write_reg(client, clientdata->reg_addr, data, 0xff); /* write all register, it is up to user to do R-mod-W */
return count;
}
static ssize_t raw_hex_all_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int low_addr=0,reg,data,rc,len=0, count=PAGE_SIZE;
struct i2c_client *client = to_i2c_client(dev);
// struct si5338_data_t *clientdata= i2c_get_clientdata(client);
for (reg=low_addr;reg<=LAST_REG;reg++) if (count>10){
if ((reg & 0xf) ==0){
rc=sprintf(buf, "%03x: ",reg);
buf+=rc;
len+=rc;
count-=rc;
}
data= read_reg(client, reg); //ignore errors
if (data<0) rc=sprintf(buf, "??");
else rc=sprintf(buf, "%02x",data);
buf+=rc;
len+=rc;
count-=rc;
if (((reg & 0xf) == 0xf) || (reg==LAST_REG)){
rc=sprintf(buf, "\n");
} else {
rc=sprintf(buf, " ");
}
buf+=rc;
len+=rc;
count-=rc;
}
return len;
}
static ssize_t raw_hex_adwe_help_show (struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Setting one/multiple registers with masks in the form [0x]AAADDWW, where AAA is register address\n" \
"DD - data byte and WW - write enable bits ( 1 - write, 0 - keep old)\n" \
"When read, provides current register data that can be used in device tree.\n");
}
//static const u32 register_masks[]= {
static ssize_t raw_hex_adwe_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int i,data;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
for (i=0;i>8)))<0) return data;
buf+=sprintf(buf," 0x%x",((register_masks[i] & 0x1ff00)<<8) | (register_masks[i] & 0xff) | ((data & 0xff)<<8));
if (((i+1) & 0x7)==0) buf+=sprintf(buf,"\n");
}
buf+=sprintf(buf,"\n");
return buf-cp;
}
/*
* accepts single or multiple data, each [0x]AAADDWW - AAA - register address, DD - data byte, WW - write enable mask (1 - write, 0 - keep).
* Ignores any other characters, so same format as in dts with hex data is OK
*/
static ssize_t raw_hex_adwe_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
const char hex_digits[]="0123456789abcdefABCDEF";
struct i2c_client *client = to_i2c_client(dev);
struct si5338_data_t *clientdata= i2c_get_clientdata(client);
int adwe,rc=0;
int left=count,num_bytes;
const char * cp;
mutex_lock(&clientdata->lock);
while ((left>0) && ((cp=strpbrk(buf,hex_digits))) && cp[0]){
left -= (cp-buf);
buf = cp;
dev_dbg(dev,"left=%d", left);
sscanf(buf, "%x%n", &adwe,&num_bytes);
left-=num_bytes;
buf+=num_bytes;
dev_dbg(dev,"left=%d num_bytes=%d, adwe=0x%08x", left,num_bytes,adwe);
if (((rc=write_adwe(client, adwe)))<0) {
mutex_unlock(&clientdata->lock);
return rc;
}
}
mutex_unlock(&clientdata->lock);
return count;
}
static ssize_t input_xtal_freq_txt_show (struct device *dev, struct device_attribute *attr, char *buf)
{
const char *txt[]= {"8MHz..11Mhz", "11MHz..19Mhz", "19MHz..26Mhz", "26MHz..30Mhz"};
struct i2c_client *client = to_i2c_client(dev);
int data= read_field (client, AWE_XTAL_FREQ);
return sprintf(buf, "%s\n",(data>=0)?txt[data]:"error");
}
static ssize_t in_frequency12_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
s64 freq= get_in_frequency (client,0);
if (freq<0) return -EINVAL;
return sprintf(buf, "%lld\n",freq);
}
static ssize_t in_frequency3_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
s64 freq= get_in_frequency (client,1);
if (freq<0) return -EINVAL;
return sprintf(buf, "%lld\n",freq);
}
static ssize_t in_frequency4_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
s64 freq= get_in_frequency (client,2);
if (freq<0) return -EINVAL;
return sprintf(buf, "%lld\n",freq);
}
static ssize_t in_frequency56_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
s64 freq= get_in_frequency (client,3);
if (freq<0) return -EINVAL;
return sprintf(buf, "%lld\n",freq);
}
static ssize_t in_frequency12_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc;
u64 freq;
struct i2c_client *client = to_i2c_client(dev);
sscanf(buf, "%lld", &freq);
if (((rc=set_in_frequency (client, freq,0)))<0) return rc;
return count;
}
static ssize_t in_frequency12xo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc;
u64 freq;
struct i2c_client *client = to_i2c_client(dev);
sscanf(buf, "%lld", &freq);
if (((rc=set_in_frequency (client, freq,4)))<0) return rc;
return count;
}
static ssize_t in_frequency3_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc;
u64 freq;
struct i2c_client *client = to_i2c_client(dev);
sscanf(buf, "%lld", &freq);
if (((rc=set_in_frequency (client, freq,1)))<0) return rc;
return count;
}
static ssize_t in_frequency4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc;
u64 freq;
struct i2c_client *client = to_i2c_client(dev);
sscanf(buf, "%lld", &freq);
if (((rc=set_in_frequency (client, freq,2)))<0) return rc;
return count;
}
static ssize_t in_frequency56_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc;
u64 freq;
struct i2c_client *client = to_i2c_client(dev);
sscanf(buf, "%lld", &freq);
if (((rc=set_in_frequency (client, freq,3)))<0) return rc;
return count;
}
static ssize_t in_p12_div_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int div, chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name)+1;
if (((div=get_in_pdiv(client,chn)))<0) return div;
return sprintf(buf, "%d\n",div);
}
static ssize_t in_p12_div_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int div,rc,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name)+1;
sscanf(buf, "%d", &div);
if (((rc=set_in_pdiv(client, div,chn)))<0) return rc;
return count;
}
static ssize_t in_mux_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data;
struct i2c_client *client = to_i2c_client(dev);
if (((data=get_in_mux(client)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t in_mux_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int data,rc;
sscanf(buf, "%d", &data);
if (((rc=set_in_mux(client, data)))<0) return rc;
return count;
}
static ssize_t in_mux_txt_show (struct device *dev, struct device_attribute *attr, char *buf)
{
const char *mux_txt[]={"IN1/IN2(diff)","IN3(single ended)","IN1/IN2(xtal)"};
int data;
struct i2c_client *client = to_i2c_client(dev);
if (((data=get_in_mux(client)))<0) return data;
return sprintf(buf, "%s\n",mux_txt[data]);
}
static ssize_t fb_mux_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data;
struct i2c_client *client = to_i2c_client(dev);
if (((data=get_fb_mux(client)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t fb_mux_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int data,rc;
sscanf(buf, "%d", &data);
if (((rc=set_fb_mux(client, data)))<0) return rc;
return count;
}
static ssize_t fb_mux_txt_show (struct device *dev, struct device_attribute *attr, char *buf)
{
const char *mux_fb_txt[]={"IN5/IN6(diff)","IN4(single ended)","No clock"};
int data;
struct i2c_client *client = to_i2c_client(dev);
if (((data=get_fb_mux(client)))<0) return data;
return sprintf(buf, "%s\n",mux_fb_txt[data]);
}
static ssize_t in_pfd_ref_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data;
struct i2c_client *client = to_i2c_client(dev);
if (((data=get_in_pfd_ref_fb(client,0)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t in_pfd_ref_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int data,rc;
sscanf(buf, "%d", &data);
if (((rc=set_in_pfd_ref_fb(client, data,0)))<0) return rc;
return count;
}
static ssize_t in_pfd_ref_txt_show (struct device *dev, struct device_attribute *attr, char *buf)
{
const char *pfd_ref_txt[]={"p1div_in(refclk)","p2div_in(fbclk)","p1div_out(refclk)","p2div_out(fbclk)","xoclk","noclk"};
int data;
struct i2c_client *client = to_i2c_client(dev);
if (((data=get_in_pfd_ref_fb(client,0)))<0) return data;
return sprintf(buf, "%s\n",pfd_ref_txt[data]);
}
static ssize_t fb_external_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data;
struct i2c_client *client = to_i2c_client(dev);
if (((data= get_fb_external(client)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t fb_external_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int data,rc;
sscanf(buf, "%d", &data);
if (((rc=set_fb_external(client, data)))<0) return rc;
return count;
}
static ssize_t in_pfd_fb_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data;
struct i2c_client *client = to_i2c_client(dev);
if (((data=get_in_pfd_ref_fb(client,1)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t in_pfd_fb_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int data,rc;
sscanf(buf, "%d", &data);
if (((rc=set_in_pfd_ref_fb(client, data,1)))<0) return rc;
return count;
}
static ssize_t in_pfd_fb_txt_show (struct device *dev, struct device_attribute *attr, char *buf)
{
const char *pfd_fb_txt[]={"p2div_in(fbclk)","p1div_in(refclk)","p2div_out(fbclk)","p1div_out(refclk)","reserved","noclk"};
int data;
struct i2c_client *client = to_i2c_client(dev);
if (((data=get_in_pfd_ref_fb(client,1)))<0) return data;
return sprintf(buf, "%s\n",pfd_fb_txt[data]);
}
static ssize_t pll_ref_frequency_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
s64 pll_in_freq= get_pll_in_frequency(client);
if (pll_in_freq<0) return (int) pll_in_freq;
return sprintf(buf, "%lld\n",pll_in_freq);
}
static ssize_t pll_fb_frequency_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
s64 pll_fb_freq= get_pll_fb_frequency(client);
if (pll_fb_freq<0) return (int) pll_fb_freq;
return sprintf(buf, "%lld\n",pll_fb_freq);
}
static ssize_t ms_p123_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int rc,chn;
u32 p123[3];
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (attr->attr.name[2]=='n') chn=4; /* exception for msn */
if (((rc=get_ms_p123(client,p123, chn)))<0) return rc;
// return sprintf(buf, "%ld %ld %ld\n",p123[0],p123[1],p123[2]);
return sprintf(buf, "%u %u %u\n",p123[0],p123[1],p123[2]);
}
static ssize_t ms_p123_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc,chn;
u32 p123[3];
int num_items;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (attr->attr.name[2]=='n') chn=4; /* exception for msn */
num_items=sscanf(buf, "%u %u %u", &p123[0], &p123[1], &p123[2]);
if (num_items<3){
p123[1]=0;
p123[2]=1;
}
if (((rc=set_ms_p123(client,p123, chn)))<0) return rc;
if (chn<4){
if (((rc=disable_spread_spectrum(client,chn)))<0) return rc;
}
return count;
}
static ssize_t ms_abc_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int rc,chn;
u32 p123[3];
u64 ms[3];
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (chn<0) chn=4; /* exception for msn - should have no digits*/
if (((rc=get_ms_p123(client,p123, chn)))<0) return rc;
p123_to_ms(ms,p123);
return sprintf(buf, "%lld %lld %lld\n",ms[0],ms[1],ms[2]);
}
static ssize_t ms_abc_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc,chn;
u32 p123[3];
u64 ms[3];
int num_items;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (chn<0) chn=4; /* exception for msn - should have no digits*/
num_items=sscanf(buf, "%lld %lld %lld", &ms[0], &ms[1], &ms[2]);
if (num_items<3){
ms[1]=0;
ms[2]=1;
} else {
remove_common_factor(&ms[1]);
}
ms_to_p123(ms,p123);
if (((rc=set_ms_p123(client,p123, chn)))<0) return rc;
if (chn<4){
if (((rc=disable_spread_spectrum(client,chn)))<0) return rc;
}
return count;
}
static ssize_t ms_pwr_states_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn, i;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
for (chn=0;chn<4;chn++){
for (i=0; ms_pwr_states[i]; i++) if (strcmp(attr->attr.name,ms_pwr_states[i]) == 0) {
if (i== get_ms_powerdown(client, chn)){
buf+=sprintf(buf," %d",chn);
break;
}
}
}
buf+=sprintf(buf,"\n");
return buf-cp;
}
static ssize_t ms_pwr_states_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, num_bytes,rc;
while ((rc=sscanf(buf, "%d%n", &chn,&num_bytes))){
dev_dbg(dev,"buf=%s rc==%d chn=%d num_bytes=%d", buf, rc,chn,num_bytes);
buf+=num_bytes;
if (((rc=set_ms_pwr_states(dev, attr->attr.name, chn)))<0) return rc;
}
return count;
}
static int set_ms_pwr_states(struct device *dev, const char * name, int chn)
{
int i,rc;
struct i2c_client *client = to_i2c_client(dev);
dev_dbg(dev,"name=%s chn=%d", name,chn);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
for (i=0; ms_pwr_states[i]; i++) if (strcmp(name,ms_pwr_states[i]) == 0) {
if (((rc=set_ms_powerdown(client, i, chn)))<0) return rc;
return 0;
}
return -EINVAL;
}
static int get_ms_powerup_state(struct device *dev, char * buf, int chn)
{
int index;
struct i2c_client *client = to_i2c_client(dev);
if (((index=get_ms_powerdown(client,chn)))<0) return index;
return sprintf (buf,ms_pwr_states[index]);
}
static ssize_t ms_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
reset_ms(client, 10);
return count;
}
static ssize_t ss_change_freq_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int mode;
struct i2c_client *client = to_i2c_client(dev);
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
mode=clientdata->ss_on_freq_change;
switch (mode) {
case 0: return sprintf(buf, "%d - turn spread spectrum off on frequency change\n",mode);
case 1: return sprintf(buf, "%d - recalculate spread spectrum on frequency change, do not reset MS\n",mode);
case 2: return sprintf(buf, "%d - turn spread spectrum off on frequency change, reset MS when SS is turned on\n",mode);
case 3: return sprintf(buf, "%d - recalculate spread spectrum on frequency change, do not reset MS\n",mode);
default: return sprintf(buf, "%d - invalid mode\n",mode);
}
}
static ssize_t ss_change_freq_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int num_items, mode;
struct i2c_client *client = to_i2c_client(dev);
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
num_items=sscanf(buf, "%d", &mode);
if (num_items && (mode>=0) && (mode<=3)){
clientdata->ss_on_freq_change=mode;
return count;
}
return -EINVAL;
}
static ssize_t ss_vals_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn,len;
if (((chn=get_chn_from_name(attr->attr.name)))<0) return chn;
if (((len= get_ss_vals(dev, buf, chn)))<0) return len;
sprintf (buf+len,"\n");
return len+1;
}
static ssize_t ss_vals_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, rc, state, num_items;
u32 rate,amp;
struct i2c_client *client = to_i2c_client(dev);
if (((chn=get_chn_from_name(attr->attr.name)))<0) return chn;
/* get current values */
if (((state= get_ss_state(client, chn)))<0) return state;
if (((rate= get_ss_down_rate(client, chn)))<0) return rate;
if (((amp= get_ss_down_amplitude(client, chn)))<0) return amp;
num_items=sscanf(buf, "%d %d %d", &state, &, &rate);
if (num_items>1){
if (((rc= store_ss_down_parameters(client, rate, amp, chn)))<0) return rc;
}
if (num_items>0){
if (state) {
/* calculate and set SS registers */
if (((rc=set_ss_down(client, chn)))<0) return rc;
/* enable SS, optionally reset MS */
if (((rc=enable_spread_spectrum(client, chn)))<0) return rc;
} else {
if (((rc=disable_spread_spectrum(client, chn)))<0) return rc;
}
}
return count;
}
static ssize_t ss_regs_hex_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn, rc;
u32 regs[7];
u32 *updown_reg, *up_regs, *down_regs;
struct i2c_client *client = to_i2c_client(dev);
updown_reg=®s[0];
down_regs=®s[1];
up_regs=®s[4];
if (((chn=get_chn_from_name(attr->attr.name)))<0) return chn;
if (((rc= get_ss_regs(client, up_regs, down_regs, updown_reg, chn)))<0) return rc;
return sprintf(buf, "updown_par=0x%x down_pars=0x%x 0x%x 0x%x up_pars= 0x%x 0x%x 0x%x\n",
regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6]);
}
static ssize_t ss_regs_hex_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, rc, num_items;
u32 regs[7];
u32 *updown_reg, *up_regs, *down_regs;
struct i2c_client *client = to_i2c_client(dev);
updown_reg=®s[0];
down_regs=®s[1];
up_regs=®s[4];
if (((chn=get_chn_from_name(attr->attr.name)))<0) return chn;
if (((rc= get_ss_regs(client, up_regs, down_regs, updown_reg, chn)))<0) return rc;
num_items=sscanf(buf, "%x %x %x %x %x %x %x", ®s[0], ®s[1], ®s[2], ®s[3], ®s[4], ®s[5], ®s[6]);
if (num_items>0){
if (num_items<5) up_regs=NULL;
if (num_items<2) down_regs=NULL;
if (((rc= set_ss_regs(client, up_regs, down_regs, updown_reg, chn)))<0) return rc;
}
return count;
}
static ssize_t pre_init_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc,clear_all;
struct i2c_client *client = to_i2c_client(dev);
clear_all=strstr(attr->attr.name,"clear")?1:0;
if (((rc=pre_init(client,clear_all)))<0) return rc;
return count;
}
static ssize_t post_init_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int rc,timeout=0;
sscanf(buf, "%d", &timeout);
if (timeout <=0) timeout=INIT_TIMEOUT;
if (((rc=post_init(client,timeout)))<0) return rc;
return count;
}
static ssize_t pll_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int rc;
u64 pll_freq[3];
struct i2c_client *client = to_i2c_client(dev);
if (((rc=get_pll_freq(client,pll_freq)))<0) return rc;
return sprintf(buf, "%lld %lld %lld\n",pll_freq[0],pll_freq[1],pll_freq[2]);
}
static ssize_t pll_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int rc,int_div,by_out;
u64 freq[3];
int num_items;
int_div=(strstr(attr->attr.name,"_fract"))?0:1; /* if filename contains '_fract' - 0, not - 1 */
by_out=(strstr(attr->attr.name,"_by_out"))?1:0; /* if filename contains '_by_out' - 1, not - 0 */
num_items=sscanf(buf, "%lld %lld %lld", &freq[0], &freq[1], &freq[2]);
if (num_items<3){
freq[1]=0;
freq[2]=1;
}
if (by_out) {
if (((rc=set_pll_freq_by_out(client, freq, int_div)))<0) return rc;
} else {
if (((rc=set_pll_freq (client, freq, int_div)))<0) return rc;
}
if (((rc=set_pll_paremeters(client)))<0) return rc;
/* if (((rc=set_misc_registers(client)))<0) return rc;*/ /* moved to pre_init() */
return count;
}
static ssize_t ms_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int rc,chn;
u64 ms_freq[3];
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((rc=get_pll_ms_freq(client, ms_freq, chn)))<0) return rc;
return sprintf(buf, "%lld %lld %lld\n",ms_freq[0],ms_freq[1],ms_freq[2]);
}
static ssize_t ms_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc,chn,int_div;
u64 freq[3];
int num_items;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
int_div=(strstr(attr->attr.name,"_fract"))?0:1; /* if includes 'fract' - 0, not - 1 */
num_items=sscanf(buf, "%lld %lld %lld", &freq[0], &freq[1], &freq[2]);
if (num_items<3){
freq[1]=0;
freq[2]=1;
}
if (((rc=set_pll_ms_by_out(client, freq, chn, int_div)))<0) return rc;
return count;
}
/* -----------Output section--------------------------- */
static ssize_t out_source_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int out_src,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((out_src=get_out_source(client, chn)))<0) return out_src;
return sprintf(buf, "%d\n",out_src);
}
static ssize_t out_source_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int rc,chn;
int out_src;
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
sscanf(buf, "%d", &out_src);
if (((rc=set_out_source(client, chn, out_src)))<0) return rc;
return count;
}
static ssize_t out_source_txt_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int out_src,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((out_src=get_out_source(client, chn)))<0) return out_src;
switch (out_src){
case 0: return sprintf(buf, "p2div_in\n");
case 1: return sprintf(buf, "p1div_in\n");
case 2: return sprintf(buf, "p2div_out\n");
case 3: return sprintf(buf, "p1div_out\n");
case 4: return sprintf(buf, "xoclk\n");
case 5: return sprintf(buf, "MS0\n");
case 6: return sprintf(buf, "MS%d\n",chn);
case 7: return sprintf(buf, "No clock\n");
}
return -EINVAL;
}
static ssize_t out_source_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int rc,chn;
u64 out_source_freq[3];
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((rc=get_output_src_frequency(client, out_source_freq, chn)))<0) return rc;
return sprintf(buf, "%lld %lld %lld\n",out_source_freq[0],out_source_freq[1],out_source_freq[2]);
}
static ssize_t out_div_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int div,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((div=get_out_div(client,chn)))<0) return div;
return sprintf(buf, "%d\n",div);
}
static ssize_t out_div_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int div,rc,chn;
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
sscanf(buf, "%d", &div);
if (((rc=set_out_div(client, div,chn)))<0) return rc;
return count;
}
static ssize_t out_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int rc,chn;
u64 out_freq[3];
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((rc=get_out_frequency(client, out_freq, chn)))<0) return rc;
return sprintf(buf, "%lld %lld %lld\n",out_freq[0],out_freq[1],out_freq[2]);
}
static ssize_t out_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc,int_div,chn;
u64 freq[3];
int num_items;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
int_div=(strstr(attr->attr.name,"_fract"))?0:1; /* if filename contains '_fract' - 0, not - 1 */
num_items=sscanf(buf, "%lld %lld %lld", &freq[0], &freq[1], &freq[2]);
if (num_items<3){
freq[1]=0;
freq[2]=1;
}
if (((rc=set_out_frequency_and_route (client, freq, chn, int_div)))<0) return rc;
return count;
}
static ssize_t out_div_by_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int rc,chn;
u64 freq[3];
int num_items;
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
num_items=sscanf(buf, "%lld %lld %lld", &freq[0], &freq[1], &freq[2]);
if (num_items<3){
freq[1]=0;
freq[2]=1;
}
if (((rc=set_out_div_by_frequency(client, freq, chn)))<0) return rc;
return count;
}
static ssize_t out_pwr_states_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn, i;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
for (chn=0;chn<4;chn++){
for (i=0; out_pwr_states[i]; i++) if (strcmp(attr->attr.name,out_pwr_states[i]) == 0) {
if (i== get_drv_powerdown(client, chn)){
buf+=sprintf(buf," %d",chn);
break;
}
}
}
buf+=sprintf(buf,"\n");
return buf-cp;
}
static ssize_t out_pwr_states_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, num_bytes,rc;
while ((rc=sscanf(buf, "%d%n", &chn,&num_bytes))){
dev_dbg(dev,"buf=%s rc==%d chn=%d num_bytes=%d", buf, rc,chn,num_bytes);
buf+=num_bytes;
if (((rc=set_out_pwr_states(dev, attr->attr.name, chn)))<0) return rc;
}
return count;
}
static int set_out_pwr_states(struct device *dev, const char * name, int chn)
{
int i,rc;
struct i2c_client *client = to_i2c_client(dev);
dev_dbg(dev,"name=%s chn=%d", name,chn);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
for (i=0; out_pwr_states[i]; i++) if (strcmp(name,out_pwr_states[i]) == 0) {
if (((rc=set_drv_powerdown(client, i, chn)))<0) return rc;
return 0;
}
return -EINVAL;
}
static int get_powerup_state(struct device *dev, char * buf, int chn)
{
int index;
struct i2c_client *client = to_i2c_client(dev);
if (((index=get_drv_powerdown(client,chn)))<0) return index;
return sprintf (buf,out_pwr_states[index]);
}
static ssize_t out_en_states_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn, i;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
for (chn=0;chn<4;chn++){
for (i=0; out_en_states[i]; i++) if (strcmp(attr->attr.name,out_en_states[i]) == 0) {
if (i== get_drv_disable(client, chn)){
buf+=sprintf(buf," %d",chn);
break;
}
}
}
buf+=sprintf(buf,"\n");
return buf-cp;
}
static ssize_t out_en_states_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, num_bytes,rc;
while ((rc=sscanf(buf, "%d%n", &chn,&num_bytes))){
dev_dbg(dev,"buf=%s rc==%d chn=%d num_bytes=%d", buf, rc,chn,num_bytes);
buf+=num_bytes;
if (((rc=set_out_en_states(dev, attr->attr.name, chn)))<0) return rc;
}
return count;
}
static int set_out_en_states(struct device *dev, const char * name, int chn)
{
int i,rc;
struct i2c_client *client = to_i2c_client(dev);
dev_dbg(dev,"name=%s chn=%d", name,chn);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
for (i=0; out_en_states[i]; i++) if (strcmp(name,out_en_states[i]) == 0) {
if (((rc=set_drv_disable(client, i, chn)))<0) return rc;
return 0;
}
return -EINVAL;
}
static int get_enabled_state(struct device *dev, char * buf, int chn)
{
int index;
struct i2c_client *client = to_i2c_client(dev);
if (((index=get_drv_disable(client,chn)))<0) return index;
return sprintf (buf,out_en_states[index]);
}
static ssize_t out_dis_states_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn, i;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
for (chn=0;chn<4;chn++){
for (i=0; out_dis_states[i]; i++) if (strcmp(attr->attr.name,out_dis_states[i]) == 0) {
if (i== get_drv_disabled_state(client, chn)){
buf+=sprintf(buf," %d",chn);
break;
}
}
}
buf+=sprintf(buf,"\n");
return buf-cp;
}
static ssize_t out_dis_states_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, num_bytes,rc;
while ((rc=sscanf(buf, "%d%n", &chn,&num_bytes))){
dev_dbg(dev,"buf=%s rc==%d chn=%d num_bytes=%d", buf, rc,chn,num_bytes);
buf+=num_bytes;
if (((rc=set_out_dis_states(dev, attr->attr.name, chn)))<0) return rc;
}
return count;
}
static int set_out_dis_states(struct device *dev, const char * name, int chn)
{
int i,rc;
struct i2c_client *client = to_i2c_client(dev);
dev_dbg(dev,"name=%s chn=%d", name,chn);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
for (i=0; out_dis_states[i]; i++) if (strcmp(name,out_dis_states[i]) == 0) {
if (((rc=set_drv_disabled_state(client, i, chn)))<0) return rc;
return 0;
}
return -EINVAL;
}
static int get_disabled_state(struct device *dev, char * buf, int chn)
{
int index;
struct i2c_client *client = to_i2c_client(dev);
if (((index=get_drv_disabled_state(client,chn)))<0) return index;
return sprintf (buf,out_dis_states[index]);
}
#ifdef GENERATE_EXTRA
static ssize_t drv_powerdown_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((data=get_drv_powerdown(client,chn)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t drv_powerdown_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int data,rc,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
sscanf(buf, "%d", &data);
if (((rc=set_drv_powerdown(client, data, chn)))<0) return rc;
return count;
}
static ssize_t drv_disable_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((data=get_drv_disable(client,chn)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t drv_disable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int data,rc,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
sscanf(buf, "%d", &data);
if (((rc=set_drv_disable(client, data, chn)))<0) return rc;
return count;
}
static ssize_t drv_disabled_state_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((data=get_drv_disabled_state(client,chn)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t drv_disabled_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int data,rc,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
sscanf(buf, "%d", &data);
if (((rc=set_drv_disabled_state(client, data, chn)))<0) return rc;
return count;
}
static ssize_t drv_invert_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((data=get_drv_invert(client,chn)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t drv_invert_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int data,rc,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
sscanf(buf, "%d", &data);
if (((rc=set_drv_invert(client, data, chn)))<0) return rc;
return count;
}
static ssize_t drv_invert_txt_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((data=get_drv_invert(client,chn)))<0) return data;
switch (data) {
case 0:return sprintf(buf, "No inversion\n");
case 1:return sprintf(buf, "Invert A only (CMOS/SSTL,HSTL)\n");
case 2:return sprintf(buf, "Invert B only (CMOS/SSTL,HSTL)\n");
case 3:return sprintf(buf, "Invert both A and B (CMOS/SSTL,HSTL)\n");
}
return 0; /* never */
}
static ssize_t drv_type_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((data=get_drv_type(client,chn)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t drv_type_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int data,rc,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
sscanf(buf, "%d", &data);
if (((rc=set_drv_type(client, data, chn)))<0) return rc;
return count;
}
static ssize_t drv_type_txt_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((data=get_drv_type(client,chn)))<0) return data;
switch (data) {
case 0:return sprintf(buf, "reserved\n");
case 1:return sprintf(buf, "CMOS/SSTL/HSTL A enabled, B disabled\n");
case 2:return sprintf(buf, "CMOS/SSTL/HSTL A disabled, A enabled\n");
case 3:return sprintf(buf, "CMOS/SSTL/HSTL A enabled, B enabled\n");
case 4:return sprintf(buf, "LVPECL\n");
case 5:return sprintf(buf, "LVDS\n");
case 6:return sprintf(buf, "CML\n");
case 7:return sprintf(buf, "HCSL\n");
}
return 0; /* never */
}
static ssize_t drv_vdd_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((data=get_drv_vdd(client,chn)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t drv_vdd_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int data,rc,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
sscanf(buf, "%d", &data);
if (((rc=set_drv_vdd(client, data, chn)))<0) return rc;
return count;
}
static ssize_t drv_vdd_txt_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((data=get_drv_vdd(client,chn)))<0) return data;
switch (data) {
case 0:return sprintf(buf, "3.3V\n");
case 1:return sprintf(buf, "2.5V\n");
case 2:return sprintf(buf, "1.8V\n");
case 3:return sprintf(buf, "1.5V\n");
}
return 0; /* never */
}
static ssize_t drv_trim_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int data,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((data=get_drv_trim(client,chn)))<0) return data;
return sprintf(buf, "%d\n",data);
}
static ssize_t drv_auto_trim_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int data,rc,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
sscanf(buf, "%d", &data);
if (((rc=update_drv_trim(client, data, chn)))<0) return rc;
return count;
}
static ssize_t drv_trim_any_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int data,rc,chn;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
sscanf(buf, "%d", &data);
if (((rc=set_drv_trim_any(client, data, chn)))<0) return rc;
return count;
}
static ssize_t drv_txt_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int chn;
char * data;
struct i2c_client *client = to_i2c_client(dev);
chn=get_chn_from_name(attr->attr.name); /* uses first digit in the name */
if (((data=get_drv_txt(client,chn)))==NULL) return -EINVAL;
return sprintf(buf, "%s\n",data);
}
/* uses out_type and out_vddo */
static int update_drv_trim(struct i2c_client *client, int novtt, int chn) /* no Vtt - CMOS, no termination, where it matters */
{
int rc;
int out_type, out_vdd,trim=-1;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if (((out_type=get_drv_type(client,chn)))<0) return out_type;
if (((out_vdd=get_drv_vdd(client,chn)))<0) return out_vdd;
switch (out_type) {
case 1:
case 2:
case 3:
switch (out_vdd){
case 0:trim=novtt?0x17:0x04; break;
case 1:trim=novtt?0x13:0x0d; break;
case 2:trim=novtt?0x17:0x15; break;
case 3:trim=0x1f; break;
}
break;
case 4:
switch (out_vdd){
case 0:trim=0x0f; break;
case 1:trim=0x10; break;
}
break;
case 5:
switch (out_vdd){
case 0:trim=0x08; break;
case 1:trim=0x09; break;
}
break;
case 6:
switch (out_vdd){
case 0:trim=0x03; break;
case 1:
case 2:trim=0x04; break;
}
break;
case 7:
switch (out_vdd){
case 0:
case 1:
case 2:trim=0x07; break;
}
break;
}
if (trim<0){
dev_err(&client->dev, "Invalid combination of output type (%d) and voltage (%d)\n",out_type,out_vdd);
return - EINVAL;
}
return write_multireg64(client, trim, awe_drv_trim[chn]);
}
/* uses out_type, out_vddo and out_trim */
static char * get_drv_txt(struct i2c_client *client, int chn)
{
int rc;
int out_type, out_vdd,out_trim=-1;
if (((rc=_verify_output_channel(client,chn)))<0) return NULL;
if (((out_type=get_drv_type(client,chn)))<0) return NULL;
if (((out_vdd=get_drv_vdd(client,chn)))<0) return NULL;
if (((out_vdd=get_drv_trim(client,chn)))<0) return NULL;
switch (out_type) {
case 1:
switch (out_vdd){
case 0:return (out_trim & 0x10)?"3.3V CMOS, A & B":"3.3V SSTL, A only";
case 1:return (out_trim & 0x10)?"2.5V CMOS, A & B":"2.5V SSTL, A only";
case 2:return (out_trim & 0x02)?"1.8V SSTL, A & B":"1.8V CMOS, A only";
case 3:return "1.5V HSTL, A only";
}
break;
case 2:
switch (out_vdd){
case 0:return (out_trim & 0x10)?"3.3V CMOS, A & B":"3.3V SSTL, B only";
case 1:return (out_trim & 0x10)?"2.5V CMOS, A & B":"2.5V SSTL, B only";
case 2:return (out_trim & 0x02)?"1.8V SSTL, A & B":"1.8V CMOS, B only";
case 3:return "1.5V HSTL, B only";
}
break;
case 3:
switch (out_vdd){
case 0:return (out_trim & 0x10)?"3.3V CMOS, A & B":"3.3V SSTL, A & B";
case 1:return (out_trim & 0x10)?"2.5V CMOS, A & B":"2.5V SSTL, A & B";
case 2:return (out_trim & 0x02)?"1.8V SSTL, A & B":"1.8V CMOS, A & B";
case 3:return "1.5V HSTL, A & B";
}
break;
case 4:
switch (out_vdd){
case 0:return "3.3V LVPECL";
case 1:return "2.5V LVPECL";
}
break;
case 5:
switch (out_vdd){
case 0:return "3.3V CML";
case 1:return "2.5V CML";
}
break;
case 6:
switch (out_vdd){
case 0:return "3.3V LVDS";
case 1:return "2.5V LVDS";
case 2:return "1.8V LVDS";
}
break;
case 7:
switch (out_vdd){
case 0:return "3.3V HCSL";
case 1:return "2.5V HCSL";
case 2:return "1.8V HCSL";
}
break;
}
dev_err(&client->dev, "Invalid combination of output type (%d) and voltage (%d)\n",out_type,out_vdd);
switch (out_vdd){
case 0:return "3.3V - invalid type";
case 1:return "2.5V - invalid type";
case 2:return "1.8V - invalid type";
case 3:return "1.5V - invalid type";
}
return NULL; /* never */
}
#endif
/* -----------Output section--------------------------- */
static int remove_common_factor(u64 * num_denom)
{
u64 a,b,r;
if (num_denom[1]==0) return -1; /* zero denominator */
if (num_denom[0]==0) {
num_denom[1]=1;
return 1;
}
a=max(num_denom[0],num_denom[1]);
b=min(num_denom[0],num_denom[1]);
r=b;
while (r>1) {
r=a-b*div64_u64(a,b);
if (r==0){
num_denom[0]=div64_u64(num_denom[0],b);
num_denom[1]=div64_u64(num_denom[1],b);
return 1;
}
a=b;
b=r;
}
return 0; /* nothing done */
}
static int _verify_output_channel(struct i2c_client *client,int chn)
{
if ((chn<0) || (chn>3)){
dev_err(&client->dev, "Invalid output channel: %d (only 0..3 are allowed)\n",chn);
return - EINVAL;
}
return 0;
}
static int get_ss_vals(struct device *dev, char * buf, int chn)
{
int state;
u32 rate,amp;
struct i2c_client *client = to_i2c_client(dev);
if (((state= get_ss_state(client, chn)))<0) return state;
if (((amp= get_ss_down_amplitude(client, chn)))<0) return amp;
if (((rate= get_ss_down_rate(client, chn)))<0) return rate;
return sprintf(buf, "Spread spectrum is %s, down amplitude= %d ( *0.01%%), spread rate= %d Hz", state?"ON":"OFF", amp, rate);
}
static int get_ss_state(struct i2c_client *client, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
return read_field(client, awe_ms_ssmode[chn]);
}
static int set_ss_state(struct i2c_client *client, int state, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
return write_field(client, state , awe_ms_ssmode[chn]);
}
static int get_ss_down_rate(struct i2c_client *client, int chn)
{
int rc;
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
return clientdata->spread_spectrum_rate[chn]; /* in Hz */
}
static int get_ss_down_amplitude(struct i2c_client *client, int chn)
{
int rc;
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
return clientdata->spread_spectrum_amp[chn];
}
/* store required parameters - they will be needed to recalculate SS registers after MS frequency change */
static int store_ss_down_parameters(struct i2c_client *client, u32 rate, u32 amp, int chn) /* chn 0,1,2,3 */
{
int rc;
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if ((rate < SPREAD_RATE_MIN) || (rate > SPREAD_RATE_MAX)){
dev_err(&client->dev, "Invalid spread spectrum rate %u - should be in [%u,%u]Hz\n",rate,SPREAD_RATE_MIN,SPREAD_AMP_MAX);
return - EINVAL;
}
if ((amp < SPREAD_AMP_MIN) || (amp > SPREAD_AMP_MAX)){
dev_err(&client->dev, "Invalid spread spectrum amplitude %u - should be in [%u,%u] (*0.01%%)\n",amp,SPREAD_AMP_MIN,SPREAD_AMP_MAX);
return - EINVAL;
}
clientdata->spread_spectrum_rate[chn]=rate; /* in Hz */
clientdata->spread_spectrum_amp[chn]=amp; /* in 0.01% */
return 0;
}
/* recalculate and set SS registers, disable SS if invalid */
static int set_ss_down(struct i2c_client *client, int chn) /* chn 0,1,2,3 */
{
int rc;
u32 ssud,ssup[3],ssdn[3];
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
/* calculate spread spectrum registers */
if (((rc=calc_ss_down_to_regs(client, ssup, ssdn, &ssud, chn)))<0) return rc;
if (rc!=0){
return disable_spread_spectrum(client,chn); /* SS parameters were never set*/
}
/* set spread spectrum registers */
if (((rc=set_ss_regs(client, ssup, ssdn, &ssud, chn)))<0) return rc;
#if 0
/* enable spread spectrum mode */
if (((rc=enable_spread_spectrum(client, chn)))<0) return rc;
#endif
return 0;
}
static int ss_pre_freq_change(struct i2c_client *client, int chn) /* chn 0,1,2,3 */
{
int rc;
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if ((clientdata->ss_on_freq_change & 1)==0) {
dev_dbg(&client->dev, "Disabling spread spectrum before changing MS%d divider",chn);
if (((rc=disable_spread_spectrum(client, chn)))<0) return rc;
}
return 0;
}
static int ss_post_freq_change(struct i2c_client *client, int chn) /* chn 0,1,2,3 */
{
int rc, ss_state;
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if (((ss_state=get_ss_state(client,chn)))<0) return ss_state;
if (ss_state){
/* recalculate and set SS registers */
dev_dbg(&client->dev, "Recalculating spread spectrum after changing MS%d divider",chn);
if (((rc=set_ss_down(client,chn)))<0) return rc;
if (clientdata->ss_on_freq_change & 2) {
reset_ms(client, 10);
}
}
return 0;
}
static int calc_ss_down_to_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn) /* chn 0,1,2,3 */
{
int rc;
u32 ssud;
u64 ms_freq, xy[2];
u32 p123[3];
u64 ms[3];
u32 rate, amp;
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
rate=clientdata->spread_spectrum_rate[chn]; /* in Hz */
amp=clientdata->spread_spectrum_amp[chn]; /* in 0.01% */
dev_dbg(&client->dev,"rate=%d, amp=%d\n",rate,amp);
if ((rate==0) || (amp==0)) return 1; /* Should disable SS */
if ((rate < SPREAD_RATE_MIN) || (rate > SPREAD_RATE_MAX)){
dev_err(&client->dev, "*Invalid spread spectrum rate %u - should be in [%u,%u]Hz\n",rate,SPREAD_RATE_MIN,SPREAD_AMP_MAX);
return - EINVAL;
}
if ((amp < SPREAD_AMP_MIN) || (amp > SPREAD_AMP_MAX)){
dev_err(&client->dev, "*Invalid spread spectrum amplitude %u - should be in [%u,%u] (*0.01%%)\n",amp,SPREAD_AMP_MIN,SPREAD_AMP_MAX);
return - EINVAL;
}
if (((rc=get_pll_ms_freq(client, &ms_freq, chn)))<0) return rc;
if (ms_freq==0){
dev_err(&client->dev, "MS%d frequency is not set, can not apply spread spectrum\n",chn);
return - EINVAL;
}
if (((rc=get_ms_p123(client,p123, chn)))<0) return rc;
p123_to_ms(ms,p123);
ssud=(u32) div64_u64(ms_freq,rate << 2);
if (updown_reg) updown_reg[0]= ssud;
if (down_regs){
xy[0]=6400000000LL*amp*(ms[0]*ms[2]+ms[1]);
xy[0]=div64_u64(xy[0],ms[2]);
xy[1]= 100000000LL*(10000-amp)*ssud;
dev_dbg(&client->dev,"x=0x%llx, y=0x%llx,\n",xy[0],xy[1]);
remove_common_factor(xy);
dev_dbg(&client->dev,"x=0x%llx, y=0x%llx,\n",xy[0],xy[1]);
down_regs[0]= (u32) div64_u64(xy[0],xy[1]);
down_regs[2]= (u32)xy[1];
down_regs[1]= (u32)xy[0] - down_regs[2]*down_regs[0];
}
if (up_regs){
up_regs[0]=0;
up_regs[1]=1;
up_regs[2]=0;
}
dev_dbg(&client->dev,"updown=0x%x, down[0]=0x%x, down[1]=0x%x, down[2]=0x%x, up[0]=0x%x, up[1]=0x%x, up[2]=0x%x\n",
updown_reg[0],down_regs[0],down_regs[1],down_regs[2],up_regs[0],up_regs[1],up_regs[2]);
return 0;
}
static int get_ss_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn) /* chn 0,1,2,3 */
{
int i;
s64 rc;
if (((rc=_verify_output_channel(client,chn)))<0) return (int) rc;
if (up_regs) for (i=0;i<3;i++){
if (((rc=read_multireg64 (client, awe_msx_ssup[chn][i])))<0) return (int) rc;
up_regs[i]= (u32) rc;
}
if (down_regs) for (i=0;i<3;i++){
if (((rc=read_multireg64 (client, awe_msx_ssdn[chn][i])))<0) return (int) rc;
down_regs[i]= (u32) rc;
}
if (updown_reg) {
if (((updown_reg[0]=read_multireg64 (client, awe_msx_ssud[chn])))<0) return (int) updown_reg[0];
}
return 0;
}
static int set_ss_regs(struct i2c_client *client, u32 * up_regs, u32 * down_regs, u32 * updown_reg, int chn) /* chn 0,1,2,3, */
{
int i,rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if (up_regs){
dev_dbg(&client->dev,"up[0]=0x%x, up[1]=0x%x, up[2]=0x%x\n",up_regs[0],up_regs[1],up_regs[2]);
for (i=0;i<3;i++){
if (((rc=write_multireg64 (client, (u64) up_regs[i], awe_msx_ssup[chn][i])))<0) return rc;
}
}
if (down_regs) {
dev_dbg(&client->dev,"down[0]=0x%x, down[1]=0x%x, down[2]=0x%x\n",down_regs[0],down_regs[1],down_regs[2]);
for (i=0;i<3;i++){
if (((rc=write_multireg64 (client, (u64) down_regs[i], awe_msx_ssdn[chn][i])))<0) return rc;
}
}
if (updown_reg) {
dev_dbg(&client->dev,"updown=0x%x\n",updown_reg[0]);
if (((rc=write_multireg64 (client, (u64) updown_reg[0], awe_msx_ssud[chn])))<0) return rc;
}
return 0;
}
static int disable_spread_spectrum(struct i2c_client *client,int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
/* disable spread spectrum - only this register was changed to 0 from default 1 */
if (((rc=write_multireg64(client, 0 , awe_msx_ssup[chn][2])))<0) return rc;
if (((rc=set_ss_state(client, 0, chn)))<0) return rc;
return 0;
}
static int enable_spread_spectrum(struct i2c_client *client,int chn)
{
int rc;
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if (((rc=set_ss_state(client, 1, chn)))<0) return rc;
if (clientdata->ss_on_freq_change & 2) {
if (((rc=reset_ms(client, 10)))<0) return rc; /* 10 - wait cycles */
}
return 0;
}
static int get_drv_powerdown(struct i2c_client *client, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
return read_field (client, awe_drv_powerdown[chn]);
}
static int set_drv_powerdown(struct i2c_client *client, int typ, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if (typ) typ=1;
return write_field (client, (u8) typ, awe_drv_powerdown[chn]);
}
static int get_drv_disable(struct i2c_client *client, int chn)
{
int rc;
if ((chn!=4) && (((rc=_verify_output_channel(client,chn)))<0)) return rc;
return read_field (client, awe_drv_disable[chn]);
}
static int set_drv_disable(struct i2c_client *client, int typ, int chn)
{
int rc;
if ((chn!=4) && (((rc=_verify_output_channel(client,chn)))<0)) return rc;
if (typ) typ=1;
return write_field (client, (u8) typ, awe_drv_disable[chn]);
}
static int get_drv_disabled_state(struct i2c_client *client, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
return read_field (client, awe_drv_dis_state[chn]);
}
static int set_drv_disabled_state(struct i2c_client *client, int typ, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if ((typ<0) || (typ>3)){
dev_err(&client->dev, "Invalid disabled state %d. Only 0..3 are supported\n",typ);
return - EINVAL;
}
return write_field (client, (u8) typ, awe_drv_dis_state[chn]);
}
static int get_drv_invert(struct i2c_client *client, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
return read_field (client, awe_drv_invert[chn]);
}
static int set_drv_invert(struct i2c_client *client, int typ, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if ((typ<0) || (typ>3)){
dev_err(&client->dev, "Invalid invert drivers %d. Only 0..3 are supported\n",typ);
return - EINVAL;
}
return write_field (client, (u8) typ, awe_drv_invert[chn]);
}
static int get_drv_type(struct i2c_client *client, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
return read_field (client, awe_drv_fmt[chn]);
}
static int set_drv_type(struct i2c_client *client, int typ, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if ((typ<0) || (typ>7)){
dev_err(&client->dev, "Invalid output type %d. Only 0..7 are supported\n",typ);
return - EINVAL;
}
return write_field (client, (u8) typ, awe_drv_fmt[chn]);
}
static int get_drv_vdd(struct i2c_client *client, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
return read_field (client, awe_drv_vddo[chn]);
}
static int set_drv_vdd(struct i2c_client *client, int vdd, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if ((vdd<0) || (vdd>7)){
dev_err(&client->dev, "Invalid output type %d. Only 0..3 are supported\n",vdd);
return - EINVAL;
}
return write_field (client, (u8) vdd, awe_drv_vddo[chn]);
}
static int get_drv_trim(struct i2c_client *client, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
return (int) read_multireg64 (client, awe_drv_trim[chn]);
}
static int set_drv_trim_any(struct i2c_client *client, int trim, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if ((trim<0) || (trim>31)){
dev_err(&client->dev, "Invalid output type %d. Only 0..31 are supported\n",trim);
return - EINVAL;
}
return write_multireg64(client, trim, awe_drv_trim[chn]);
}
static int set_out_div(struct i2c_client *client, int div, int chn) /*chn =0..3 */
{
int rc;
u8 val;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
for (val=0;valdev, "Invalid value for output divider: %d\n",div);
return - EINVAL;
}
static int get_out_div(struct i2c_client *client, int chn) /*chn =0..3 */
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if (((rc=read_field(client, awe_rdiv_k[chn])))<0) return rc;
if (rc>=ARRAY_SIZE(out_div_values)){
dev_err(&client->dev, "Invalid value for output divider: %d\n",rc);
return - EINVAL;
}
return out_div_values[rc];
}
static int set_out_div_by_frequency(struct i2c_client *client, u64* out_freq, int chn) /*chn =0..3 */
{
int rc,i,idiv;
u64 out_src_freq[3],div, div15, out_src_num,out_src_denom,out_num,out_denom;
if (((rc=get_output_src_frequency(client, out_src_freq, chn)))<0) return rc;
out_src_num=out_src_freq[0]*out_src_freq[2]+out_src_freq[1];
out_src_denom=out_src_freq[2];
out_num=out_freq[0]*out_freq[2]+out_freq[1];
out_denom=out_freq[2];
if (out_num==0){
dev_err(&client->dev, "Zero output frequency for channel: %d\n",chn);
return - EINVAL;
}
while ((out_src_denom>0x1000) || (((out_src_denom | out_src_num) &1)==0)){
out_src_denom>>=1;
out_src_num>>= 1;
}
while ((out_denom>0x1000) || (((out_denom | out_num) &1)==0)){
out_denom>>=1;
out_num>>= 1;
}
dev_dbg(&client->dev, "out_src_num=%lld, out_src_denom=%lld, out_num=%lld, out_denom=%lld, \n",
out_src_num, out_src_denom, out_num, out_denom);
out_src_num*=out_denom;
out_src_denom*=out_num;
div=div64_u64(out_src_num + (out_src_denom>>1), out_src_denom);
div15=div+(div>>1);
dev_dbg(&client->dev, "out_src_num*out_denom=%lld, out_src_denom*out_num=%lld, div=%lld, div15=%lld \n",
out_src_num, out_src_denom, div, div15);
if ((div15<1) || (div15>=64)) {
dev_err(&client->dev, "Output divider (%d) out of 1..32 range for output %d \n",(int) div,chn);
return - EINVAL;
}
idiv=(int) div15;
for (i=5;i>=0;i--) if ((1<7)){
dev_err(&client->dev, "Invalid source %d. Only 0...7 are supported\n",src);
return - EINVAL;
}
return write_field (client, (u8) src, awe_rdiv_in[chn]);
}
static int get_out_ms(struct i2c_client *client, int chn)
{
int rc,out_src;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if (((out_src=get_out_source(client, chn)))<0) return out_src;
switch (out_src){
case 5: return 0;
case 6: return chn;
}
return -1;
}
/* Examples:
* "IN12:2:4"
* "XO:1:1"
* "MS0:16"
* "NO"
*/
static int get_out_route(struct i2c_client *client, char* buf, int chn)
{
int rc;
int out_src,div1=-1,div2=-1,src_group, src;
const int in_numbers[]={12,3,4,56};
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if (((out_src=get_out_source(client, chn)))<0) return out_src;
if (((div2=get_out_div(client,chn)))<0) return div2; /* 1/2/4/8/16/32 */
switch (out_src){
case 0: /* p2div in */
case 2: /* p2div out */
if (out_src & 2) {
if (((div1=get_in_pdiv(client,1)))<0) return div1; /* 1/2/4/8/16/32 */
} else div1=1;
if (((src=get_fb_mux(client)))<0) return src;
src_group=0;
src=(src)?2:3; /* mod src: 0 - IN56, 1 - IN4 */
break;
case 1: /* p1div in */
case 3: /* p1div out */
if (out_src & 2) {
if (((div1=get_in_pdiv(client,0)))<0) return div1; /* 1/2/4/8/16/32 */
} else div1=1;
if (((src=get_in_mux(client)))<0) return src;
if (src==2){
src_group=1;
src=0;
} else {
src_group=0; /* keep src: 0 - IN12, 1 - IN3 */
}
break;
case 4: src_group=1; div1=1; break;
case 5: src_group=2; src=0; break;
case 6: src_group=2; src=chn; break;
case 7: src_group=3; break;
}
dev_dbg(&client->dev, "out_src=%d, src_group=%d, src=%d, div1=%d, div2=%d\n",out_src,src_group,src,div1,div2);
switch (src_group) {
case 0: return sprintf(buf,"IN%d:%d:%d",in_numbers[src],div1,div2);
case 1: return sprintf(buf,"XO:%d:%d",div1,div2);
case 2: return sprintf(buf,"MS%d:%d",src,div2);
case 3: return sprintf(buf,"No clock");
}
return 0;
}
static int set_out_route(struct i2c_client *client, const char* route, int chn)
{
int src_group, src, div1, div2, out_src, mux1=-1,mux2=-1;
const char *cp=route;
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
/* parse string */
if ((strncasecmp(cp,"in",2)==0)) {
cp+=2;
if ((strncmp(cp,"12",2)==0)) {
cp+=2;
src=0;
} else if ((strncmp(cp,"3", 1)==0)) {
cp+=1;
src=1;
} else if ((strncmp(cp,"4", 1)==0)) {
cp+=1;
src=2;
} else if ((strncmp(cp,"56",2)==0)){
cp+=2;
src=3;
} else return -EINVAL; /* invalid input number(s) */
src_group=0;
} else if ((strncasecmp(cp,"xo",2)==0)) {
cp+=2;
src_group=1;
src=0;
} else if ((strncasecmp(cp,"ms",2)==0)) {
cp+=2;
src=cp[0]-'0';
cp++;
if ((src!=0) && (src!=chn)){
return -EINVAL; /* invalid MS channel */
}
src_group=2;
div1=-1;
} else if ((strncasecmp(cp,"no",2)==0)) {
cp+=2;
src_group=3;
} else return -EINVAL;
/* for IN and XO - find input divisor */
if (src_group<2){
if ((cp[0]=='/') || (cp[0]==':')){
cp++;
if ((strncmp(cp,"32",2)==0)) {
div1=5;
cp++;
} else if ((strncmp(cp,"16",2)==0)) {
div1=4;
cp++;
} else if ((strncmp(cp,"8",1)==0)) div1=3;
else if ((strncmp(cp,"4",1)==0)) div1=2;
else if ((strncmp(cp,"2",1)==0)) div1=1;
else if ((strncmp(cp,"1",1)==0)) div1=0;
else return -EINVAL;
cp++;
} else return -EINVAL; /* divisor expected */
}
/* get output divisor */
if (src_group<3){ /* not 'no clock' */
if ((cp[0]=='/') || (cp[0]==':')){
cp++;
if ((strncmp(cp,"32",2)==0)) {
div2=5;
cp++;
} else if ((strncmp(cp,"16",2)==0)) {
div2=4;
cp++;
} else if ((strncmp(cp,"8",1)==0)) div2=3;
else if ((strncmp(cp,"4",1)==0)) div2=2;
else if ((strncmp(cp,"2",1)==0)) div2=1;
else if ((strncmp(cp,"1",1)==0)) div2=0;
else return -EINVAL;
cp++;
} else return -EINVAL; /* divisor expected */
/* apply output divisor*/
if (((rc==set_out_div(client, 1<1)?0:1; /* p2div_in / p1div_in */
} else {
out_src=(src>1)?2:3; /* p2div_out / p1div_out */
if (src_group==1){ /* xo, but with division */
mux1=2;
} else {
switch (src){
case 0: mux1=0; break; /* in1/in2 */
case 1: mux1=1; break; /* in3 */
case 2: mux2=1; break; /* in4 */
case 3: mux2=0; break; /* in5/in6 */
}
}
}
}
dev_dbg(&client->dev, "src_group=%d, src=%d, div1=%d, div2=%d, mux1=%d,mux2=%d, out_src=%d \n",src_group, src, div1, div2, mux1,mux2,out_src);
if (((rc==set_out_source(client, chn,out_src)))<0) return rc;
if (div1>0){ /* only set p1div/p2div if needed */
if (((rc==set_in_pdiv(client, 1<=0){
if (((rc==set_in_mux(client, mux1)))<0) return rc; /* set input mux if it is used */
}
if (mux2>=0){
if (((rc==set_fb_mux(client, mux2)))<0) return rc; /* set fb mux if it is used */
}
return 0;
}
static int set_out_frequency_and_route (struct i2c_client *client, u64 *out_freq, int chn, int int_div)
{
/* using MS with the same number as the output, enabling power to that MS */
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
/* setup MSn division */
if (((rc=set_pll_ms_by_out(client, out_freq, chn, int_div)))<0) return rc;
/* enable power for selected MS */
if (((rc=set_ms_powerdown(client, 0, chn)))<0) return rc;
/* route MSn to the output (6 - use 'own' MS) */
if (((rc=set_out_source(client, chn, 6)))<0) return rc;
/* setup output (R) division - by 1/2/4/8/16/32 */
if (((rc=set_out_div_by_frequency(client, out_freq, chn)))<0) return rc;
/* enable power for selected output */
if (((rc=set_drv_powerdown(client, 0, chn)))<0) return rc;
/* Note: output is not enabled ! Should it be not powered up too?*/
return 0; /* all done */
}
static s64 get_output_src_frequency(struct i2c_client *client, u64 *out_freq, int chn)
{
int mux;
int div=1,rc=0;
s64 freq[3]={0,0,1};
if (((mux=get_out_source(client,chn)))<0) return mux;
switch (mux){
case 0:
freq[0]= get_p2div_in_frequency(client);
break;
case 1:
freq[0]= get_p1div_in_frequency(client);
break;
case 2:
freq[0]= get_p2div_in_frequency(client);
div=get_in_pdiv(client,1);
break;
case 3:
freq[0]= get_p1div_in_frequency(client);
div=get_in_pdiv(client,0);
break;
case 4:
freq[0]= get_in_frequency (client,0); /* IN1/IN2, xtal */
break;
case 5:
rc=get_pll_ms_freq(client, freq, 0); /* MS0 output */
break;
case 6:
rc=get_pll_ms_freq(client, freq, chn); /* MS output */
break;
case 7:
freq[0]= 0; /* No clock */
break;
default:
dev_err(&client->dev, "Invalid value for output source mux %d\n",mux);
return - EINVAL;
}
if (rc<0) return rc;
if (freq[0]<0) return freq[0];
if (div<0) return div;
out_freq[1]=freq[0]*freq[2]+freq[1];
out_freq[2]=freq[2]*div;
out_freq[0]=div64_u64(out_freq[1],out_freq[2]);
out_freq[1]-=out_freq[0]*out_freq[2];
if (out_freq[1]==0) out_freq[2]=1;
remove_common_factor(&out_freq[1]);
return 0;
}
/* -----------PLL section--------------------------- */
static u32 awe_fcal[]= {AWE_FCAL_07_00, AWE_FCAL_15_08, AWE_FCAL_17_16, 0};
static u32 awe_fcal_ovrd[]={AWE_FCAL_OVRD_07_00, AWE_FCAL_OVRD_15_08, AWE_FCAL_OVRD_17_15, 0};
static int pre_init(struct i2c_client *client, int clear_all)
{
int rc,chn;
if (((rc=set_misc_registers(client)))<0) return rc; /* setup miscelalneous registers */
if (((rc=write_field(client, 1, AWE_OUT_ALL_DIS )))<0) return rc; /* disable all outputs */
if (((rc=write_field(client, 1, AWE_DIS_LOS )))<0) return rc; /* pause LOL */
if (clear_all){ /* clears outputs pll input/fb muxes to be set later */
/* extra */
for (chn=0;chn<4;chn++){
if (((rc=disable_output(client, chn)))<0) return rc;
}
/* to be explicitly enabled if needed */
if (((rc=disable_pll_in_fb_mux(client)))<0) return rc;
}
return 0;
}
static int post_init(struct i2c_client *client, int timeout) /*1 in timeout ~ 0.1ms - i2c read register */
{
int rc,i,in_src, fb_src,ext_fb,check_los=0;
s64 fcal;
/* validate input clock status */
if (((in_src=get_in_pfd_ref_fb(client,0)))<0) return in_src;
switch (in_src){
case 0:
case 2:
case 4:
check_los |= AWE_STATUS_PLL_LOS_CLKIN; break;
case 1:
case 3:
check_los |= AWE_STATUS_PLL_LOS_FDBK; break;
}
if (((ext_fb=read_field(client,AWE_PFD_EXTFB)))<0) return ext_fb;
if (ext_fb){
if (((fb_src=get_in_pfd_ref_fb(client,1)))<0) return fb_src;
switch (in_src){
case 1:
case 3:
check_los |= AWE_STATUS_PLL_LOS_CLKIN; break;
case 0:
case 2:
check_los |= AWE_STATUS_PLL_LOS_FDBK; break;
}
}
check_los &= 0xf;
for (i=0;i=timeout){
dev_err(&client->dev, "Timeout waiting for input clocks, status=0x%x, mask=0x%x\n",rc, check_los);
return -EPIPE;
}
dev_dbg(&client->dev, "Validated input clocks, t=%d cycles (timeout= %d cycles), status =0x%x, mask=0x%x\n",
i, timeout, rc, check_los);
if (((rc=write_field(client, 0, AWE_FCAL_OVRD_EN )))<0) return rc; /* Configure PLL for locking, set FCAL_OVRD_EN=0 */
write_field(client, 1, AWE_SOFT_RESET ); /* Configure PLL for locking, set SOFT_RESET=1 (ignore i2c error) */
for (i=0;i<250;i++) get_status(client); /* wait 25 ms */
if (((rc=write_field(client, 0x65, AWE_REG241 )))<0) return rc; /* re-enable LOL, set reg 241=0x65 */
check_los |= AWE_STATUS_PLL_LOL | AWE_STATUS_PLL_SYS_CAL;
check_los &= 0xf;
for (i=0;i=timeout){
dev_err(&client->dev, "Timeout waiting for PLL lock, status=0x%x, mask=0x%x\n",rc, check_los);
return -EPIPE;
}
dev_dbg(&client->dev, "Validated PLL locked, t=%d cycles (timeout= %d cycles), status =0x%x, mask=0x%x\n",
i, timeout, rc, check_los);
/* copy FCAL values to active registers */
if (((fcal=read_multireg64 (client, awe_fcal)))<0) return (int) fcal;
if (((rc= write_multireg64(client, fcal, awe_fcal_ovrd)))<0) return rc;
dev_dbg(&client->dev, "Copied FCAL data 0x%llx\n", fcal);
if (((rc=write_field(client, 5, AWE_REG47_72 )))<0) return rc; /* set 47[7:2] to 000101b */
if (((rc=write_field(client, 1, AWE_FCAL_OVRD_EN )))<0) return rc; /* SET PLL to use FCAL values, set FCAL_OVRD_EN=1 */
/* only needed if using down-spread. Won't hurt to do anyway */
if (((rc=reset_ms(client, 10)))<0) return rc;
if (((rc=write_field(client, 0, AWE_OUT_ALL_DIS )))<0) return rc; /* enable all (enabled individually) outputs */
write_field(client, 0, AWE_SOFT_RESET ); /* Not documented - what to do with the soft reset bit - clearing */
if (((rc=power_up_down_needed_ms(client)))<0) return rc;
return 0;
}
static int reset_ms(struct i2c_client *client, int wait_cycles)
{
int i,rc;
dev_dbg(&client->dev, "Resetting MS dividers");
if (((rc=write_field(client, 1, AWE_MS_RESET )))<0) return rc; /* SET MS RESET=1 */
for (i=0;i=15){
K=925;
rsel=0;
bwsel=0;
} else if (fpfd_mhz>=8){
K=325;
rsel=1;
bwsel=1;
} else {
K=185;
rsel=3;
bwsel=2;
}
if (fvco_mhz>2425){
Q=3;
vco_gain=0;
} else {
Q=4;
vco_gain=1;
}
kphi_num= K*2500LL*2500LL*2500LL;
kphi_denom=533LL*Q*fpfd_mhz*fvco_mhz*fvco_mhz;
pll_kphi=(int) div64_u64(kphi_num + (kphi_denom>>1),kphi_denom);
if ((pll_kphi<1) || (pll_kphi>127)) {
dev_err(&client->dev, "Calculated PLL_KPHI does not fit 1<=%d<=127\n",pll_kphi);
if (pll_kphi<1) pll_kphi=1;
else if (pll_kphi>127) pll_kphi=127;
}
mscal = (int) div64_u64(2067000-667*fvco_mhz+50000,100000ll);
if ((mscal<0) || (mscal>63)) {
dev_err(&client->dev, "Calculated MSCAL does not fit 0<=%d<=63\n",mscal);
if (mscal<0) mscal=0;
else if (mscal>63) mscal=63;
}
ms_pec = 7;
dev_dbg(&client->dev, "Calculated values: PLL_KPHI=%d K=%lld RSEL=%d BWSEL=%d VCO_GAIN=%d MSCAL=%d MS_PEC=%d\n",
pll_kphi, K, rsel, bwsel, vco_gain, mscal, ms_pec);
/* setting actual registers */
if (((rc=write_field(client, (u8) pll_kphi, AWE_PLL_KPHI)))<0) return rc;
if (((rc=write_field(client, (u8) (((vco_gain & 7)<<4) | ((rsel & 3)<<2) | (bwsel & 3)),
AWE_VCO_GAIN_RSEL_BWSEL)))<0) return rc;
if (((rc=write_field(client, (u8) mscal, AWE_MSCAL )))<0) return rc;
if (((rc=write_field(client, (u8) ms_pec, AWE_MS_PEC)))<0) return rc;
if (((rc=write_field(client, 3, AWE_PLL_EN)))<0) return rc; /* enable PLL */
return 0;
}
/* verify the chip is initilaized - returns 0 if power-up state, 5 if initialized, -1 if i2c register can not be read */
static int is_set_up(struct i2c_client *client)
{
return read_field(client, AWE_MISC_47 );
}
static int set_misc_registers(struct i2c_client *client)
{
/* ST52238 Reference Manual R1.2 p.28 */
int rc;
if (((rc=write_field(client, 0x5, AWE_MISC_47 )))<0) return rc;
if (((rc=write_field(client, 0x1, AWE_MISC_106 )))<0) return rc;
if (((rc=write_field(client, 0x1, AWE_MISC_116 )))<0) return rc;
if (((rc=write_field(client, 0x1, AWE_MISC_42 )))<0) return rc;
if (((rc=write_field(client, 0x0, AWE_MISC_06A )))<0) return rc;
if (((rc=write_field(client, 0x0, AWE_MISC_06B )))<0) return rc;
if (((rc=write_field(client, 0x0, AWE_MISC_28 )))<0) return rc;
return 0;
}
/* -----------MultiSynth section--------------------------- */
static u32 awe_msx[5][3][5]=
{{{AWE_MS0_P1_07_00, AWE_MS0_P1_15_08, AWE_MS0_P1_17_16, 0, 0},
{AWE_MS0_P2_05_00, AWE_MS0_P2_13_06, AWE_MS0_P2_21_14, AWE_MS0_P2_29_22, 0},
{AWE_MS0_P3_07_00, AWE_MS0_P3_15_08, AWE_MS0_P3_23_16, AWE_MS0_P3_29_24, 0}},
{{AWE_MS1_P1_07_00, AWE_MS1_P1_15_08, AWE_MS1_P1_17_16, 0, 0},
{AWE_MS1_P2_05_00, AWE_MS1_P2_13_06, AWE_MS1_P2_21_14, AWE_MS1_P2_29_22, 0},
{AWE_MS1_P3_07_00, AWE_MS1_P3_15_08, AWE_MS1_P3_23_16, AWE_MS1_P3_29_24, 0}},
{{AWE_MS2_P1_07_00, AWE_MS2_P1_15_08, AWE_MS2_P1_17_16, 0, 0},
{AWE_MS2_P2_05_00, AWE_MS2_P2_13_06, AWE_MS2_P2_21_14, AWE_MS2_P2_29_22, 0},
{AWE_MS2_P3_07_00, AWE_MS2_P3_15_08, AWE_MS2_P3_23_16, AWE_MS2_P3_29_24, 0}},
{{AWE_MS3_P1_07_00, AWE_MS3_P1_15_08, AWE_MS3_P1_17_16, 0, 0},
{AWE_MS3_P2_05_00, AWE_MS3_P2_13_06, AWE_MS3_P2_21_14, AWE_MS3_P2_29_22, 0},
{AWE_MS3_P3_07_00, AWE_MS3_P3_15_08, AWE_MS3_P3_23_16, AWE_MS3_P3_29_24, 0}},
{{AWE_MSN_P1_07_00, AWE_MSN_P1_15_08, AWE_MSN_P1_17_16, 0, 0},
{AWE_MSN_P2_05_00, AWE_MSN_P2_13_06, AWE_MSN_P2_21_14, AWE_MSN_P2_29_22, 0},
{AWE_MSN_P3_07_00, AWE_MSN_P3_15_08, AWE_MSN_P3_23_16, AWE_MSN_P3_29_24, 0}}};
static const u32 awe_ms_powerdown[]={AWE_MS0_PDN, AWE_MS1_PDN, AWE_MS2_PDN, AWE_MS3_PDN};
static int get_ms_powerdown(struct i2c_client *client, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
return read_field (client, awe_ms_powerdown[chn]);
}
static int set_ms_powerdown(struct i2c_client *client, int typ, int chn)
{
int rc;
if (((rc=_verify_output_channel(client,chn)))<0) return rc;
if (typ) typ=1;
return write_field (client, (u8) typ, awe_ms_powerdown[chn]);
}
static int ms_to_p123(u64* ms,u32 * p123)
{
/*
* a=ms[0],b=ms[1],c=ms[2] ms~=a+b/c
* p1=floor(((a*c+b)*128)/c -512)
* p2=mod((b*128),c)
* p3=c
*/
u64 d;
u64 ms_denom=ms[2], ms_num=ms[1], ms_int=ms[0];
while ((ms_denom >= (1<<30))| (((ms_denom | ms_num) &1) == 0)) {
ms_denom >>= 1;
ms_num >>= 1;
}
if ((ms_num==0) || (ms_denom==0)){
ms_denom = 1;
ms_num = 0;
}
d= (ms_int * ms_denom + ms_num)<<7;
p123[0]= (u32) (div64_u64(d,ms_denom) -512);
d=div64_u64((ms_num<<7),ms_denom);
p123[1]= (u32) ((ms_num<<7)-d*ms_denom);
p123[2]=ms_denom;
pr_debug("ms[]=%llu + %llu/%llu Hz, ms_int=%llu, ms_num=%llu, ms_denom=%llu p123=%u %u %u\n",
ms[0],ms[1],ms[2],ms_int,ms_num,ms_denom,p123[0],p123[1],p123[2]);
return 0;
}
static int p123_to_ms(u64* ms,u32 * p123)
{
/* a=ms[0],b=ms[1],c=ms[2] ms~=a+b/c
* p1=floor(((a*c+b)*128)/c -512)
* p2=mod((b*128),c)
* p3=c
* ---
* b*128=k*c +p2; k<128, p2>7
* a= (p1+512)>>7=(p1>>7)+4
*
*/
ms[2]=p123[2]; /* c= p3 */
ms[1]=(ms[2]*(p123[0] & 0x7f) + p123[1]) >>7; /* b= (c*(p1 & 0x7f) + p2) >>7 */
ms[0]=(p123[0]>>7)+4; /* a= (p1>>7)+4 */
pr_debug("ms[]=%llu + %llu/%llu, p123=%u %u %u\n",
ms[0],ms[1],ms[2],p123[0],p123[1],p123[2]);
return 0;
}
static int get_ms_p123(struct i2c_client *client,u32 * p123, int chn) /* chn 0,1,2,3,4 (4 - msn) */
{
int i;
s64 rc;
if ((chn<0) || (chn>4)){
dev_err(&client->dev, "Invalid channel %d. Only 0,1,2,3 and 4 (for MSN) are supported\n",chn);
return - EINVAL;
}
for (i=0;i<3;i++){
if (((rc=read_multireg64 (client, awe_msx[chn][i])))<0) return (int) rc;
p123[i]= (u32) rc;
}
return 0;
}
static int set_ms_p123(struct i2c_client *client,u32 * p123, int chn) /* chn 0,1,2,3,4 (4 - msn) */
{
int i,rc,hs;
if ((chn<0) || (chn>4)){
dev_err(&client->dev, "Invalid channel %d. Only 0,1,2,3 and 4 (for MSN) are supported\n",chn);
return - EINVAL;
}
/* high speed bit programming */
if (p123[0]<512){ /* div less than 8 */
if (p123[0]<128) p123[0]=0;
else p123[0]=256;
p123[1]=0;
p123[2]=1;
dev_dbg(&client->dev, "Using high speed divisor option on ms%d",chn);
} else hs=0;
if (((rc=write_field(client, hs, awe_ms_hs[chn])))<0) return rc;
/* optionally disable spread spectrum before changing frequency */
if (chn<4){
if (((rc=ss_pre_freq_change(client, chn)))<0) return rc;
}
for (i=0;i<3;i++){
if (((rc=write_multireg64(client, (u64) p123[i], awe_msx[chn][i])))<0) return rc;
}
/* optionally enable spread spectrum after changing frequency, reset MS */
if (chn<4){
if (((rc=ss_post_freq_change(client, chn)))<0) return rc;
}
return 0;
}
/* Setting PLL frequency in 3 ways:
* 1 - specified directly, allow fractional MSN
* 2 - specified directly, integer MSN
* 3 - specified by output frequency, allow fractional MSN (use PPL frequency closest to the middle)
* 4 - specified by output frequency, integer MSN
*/
static int set_pll_freq(struct i2c_client *client, u64 *vco_freq, int int_div)
{
s64 pll_in_freq, pll_in_freq_scaled,pll_out_freq_scaled,d;
u32 msn_p123[3];
u64 msn[]={0,0,1};
s64 vco_int=vco_freq[0],vco_num=vco_freq[1],vco_denom=vco_freq[2];
if ((vco_num==0) || (vco_denom==0)){
vco_num=0;
vco_denom=1;
}
if (vco_num>=vco_denom){ /* normalize */
d=div64_u64(vco_num,vco_denom);
vco_int+=d;
vco_num-=d*vco_denom;
}
if (vco_int < FVCOMIN){
dev_err(&client->dev, "Specified PLL frequency is too low: %llu < %llu\n",vco_int, FVCOMIN);
return - EINVAL;
}
if (vco_int > FVCOMAX){
dev_err(&client->dev, "Specified PLL frequency is too high: %llu > %llu\n",vco_int, FVCOMAX);
return - EINVAL;
}
pll_in_freq=get_pll_in_frequency(client);
if (pll_in_freq<0) return (int) pll_in_freq;
pll_in_freq_scaled=pll_in_freq*vco_denom;
// pll_out_freq_scaled=pll_out_freq*vco_denom;
pll_out_freq_scaled=vco_int*vco_denom+vco_num;
msn[0]=div64_u64(pll_out_freq_scaled,pll_in_freq_scaled);
msn[1]=pll_out_freq_scaled-pll_in_freq_scaled*msn[0];
msn[2]=pll_in_freq_scaled;
while (msn[2] >= (1<<30)) { /* trim */
msn[2] >>= 1;
msn[1] >>= 1;
}
if (msn[0] < MSINT_MIN){
dev_err(&client->dev, "Calculated MSN ratio is too low: %llu < %u\n",msn[0], MSINT_MIN);
return - EINVAL;
}
if (msn[0] > MSINT_MAX){
dev_err(&client->dev, "Calculated MSN ratio is too high: %llu > %u\n",msn[0], MSINT_MAX);
return - EINVAL;
}
if (int_div){
if (msn[1]>=(msn[2]>>1)) msn[0]++; // round
msn[1] = 0;
msn[2] = 1;
} else {
remove_common_factor(&msn[1]);
}
ms_to_p123(msn, msn_p123);
return set_ms_p123(client,msn_p123, 4); // MSN
}
/* normalizes output */
static int get_pll_freq(struct i2c_client *client,u64 * pll_freq)
{
int rc;
s64 pll_in_freq;
u32 p123[3];
pll_in_freq=get_pll_in_frequency(client);
if (pll_in_freq<=0) return (int) pll_in_freq; // return 0 if in frequency==0
if (((rc=get_ms_p123(client,p123,4)))<0) return rc; /* channel4 - MSN */
p123_to_ms(pll_freq,p123);
if (pll_freq[2]<=0) return -EINVAL; /* 0 denominator */
pll_freq[1] =(pll_freq[0]*pll_freq[2]+pll_freq[1])*pll_in_freq;
pll_freq[0] =div64_u64(pll_freq[1],pll_freq[2]);
pll_freq[1]-=pll_freq[0]*pll_freq[2];
if (pll_freq[1]==0){
pll_freq[2]=1;
} else {
remove_common_factor(&pll_freq[1]);
}
return 0;
}
/*
* Calculate pll output frequency to match specified output frequency
* out_freq as int,num,denom
*/
static int set_pll_freq_by_out(struct i2c_client *client, u64 *out_freq, int int_msn_div)
{
/* use r-divider if the output frequency is too low (less than 5 MHz) */
u64 out_int=out_freq[0],out_num=out_freq[1],out_denom=out_freq[2],
pll_out_freq[3], scaled_max,scaled_min,d;
s64 pll_freq_scaled, out_freq_scaled,err,best_err=-1,center_scaled,center_diff,best_center_diff,
out_div,pll_in_freq,in_div,best_in_div, pll_in_freq_scaled,synth_out_scaled;
int r_div=1;
if (out_denom==0){
dev_err(&client->dev, "denominator should not be 0 in %lld+%lld/%lld\n",
out_int,out_num,out_denom);
return -EINVAL;
}
out_freq_scaled=out_denom*out_int+out_num;
scaled_max=(FVCOMAX/MSINT_MAX)*out_denom;
while ((r_div < 32) && (out_freq_scaleddev, "Specified output frequency is too low: %lld < %lld\n",
out_freq[0], FVCOMAX/MSINT_MAX/32);
return -EINVAL;
}
dev_dbg(&client->dev, "Output divider by %u, Output frequency before divider: %llu+%llu/%llu Hz\n",
r_div, div64_u64(out_freq_scaled,out_denom),
out_freq_scaled-out_denom*div64_u64(out_freq_scaled,out_denom),out_denom);
scaled_max=FVCOMAX*out_denom;
scaled_min=FVCOMIN*out_denom;
if (int_msn_div==0){
out_div=div64_u64( ((FVCOMAX+FVCOMIN)/2)*out_denom+(out_freq_scaled>>1),out_freq_scaled);
dev_dbg(&client->dev, "out_div=%llu out_freq_scaled=%llu out_denom= %llu scaled_max=%llu scaled_min=%lld\n",
out_div, out_freq_scaled, out_denom,scaled_max, scaled_min);
if ((out_div==7) || (out_div==5) || (out_div==3)){
if (out_freq_scaled*(out_div+1)4) && (out_freq_scaled*(out_div-1)>scaled_min)){
out_div--;
} else {
out_div=0;
}
}
dev_dbg(&client->dev, "modified out_div=%lld\n", out_div);
if ((out_div<4) || (out_div > MSINT_MAX) ||
(out_freq_scaled*out_div < scaled_min) ||
(out_freq_scaled*out_div > scaled_max)){
dev_err(&client->dev, "Can not find suitable divisor for output frequency %lld+%lld/%lld Hz\n",
div64_u64(out_freq_scaled,out_denom),
out_freq_scaled-out_denom*div64_u64(out_freq_scaled,out_denom),out_denom);
return -EINVAL;
}
pll_out_freq[0]=div64_u64(out_freq_scaled*out_div,out_denom);
pll_out_freq[1]=(out_freq_scaled*out_div)-pll_out_freq[0]*out_denom;
pll_out_freq[2]=out_denom;
dev_dbg(&client->dev, "PLL output divider by %llu, pll frequency: %llu+%llu/%llu Hz\n",
out_div,pll_out_freq[0],pll_out_freq[1],pll_out_freq[2]);
return set_pll_freq(client, pll_out_freq, 0);
} else { /* if (int_msn_div==0), find the best pair of integer coefficients, try closest to the center, if possible */
pll_in_freq=get_pll_in_frequency(client);
pll_in_freq_scaled=pll_in_freq*out_denom;
center_scaled=((FVCOMAX+FVCOMIN)>>1)*out_denom;
if (pll_in_freq<0) return (int) pll_in_freq;
best_in_div=0;
for (out_div=4;out_div<=MSINT_MAX;out_div++) if ((out_div!=5) && (out_div!=7)){
pll_freq_scaled=out_freq_scaled*out_div; /* here scaled by denominator */
if ((pll_freq_scaled>=scaled_min) && (pll_freq_scaled<=scaled_max)) {
in_div=div64_u64(pll_freq_scaled+(pll_in_freq_scaled>>1),pll_in_freq_scaled); // round
d=pll_in_freq_scaled*in_div; /* actual pll frequency scaled by out_denom */
synth_out_scaled=div64_u64(d + (out_div>>1),out_div);
center_diff=d-center_scaled;
if (center_diff<0) center_diff=-center_diff;
err=synth_out_scaled-out_freq_scaled;
if (err<0) err=-err;
if ((best_in_div==0) || (err < best_err) || ((err == best_err) && (center_diffdev, "synth_out_scaled: %lld center_scaled: %lld out_freq_scaled:%lld err: %lld (%lld) center_diff:%lld(%lld)\n",
synth_out_scaled, center_scaled, out_freq_scaled,err,best_err,center_diff,best_center_diff);
best_err=err;
best_in_div=in_div;
best_center_diff=center_diff;
}
}
}
if (best_in_div==0){
dev_err(&client->dev, "Failed to find suitable integer coefficients for pll input %lld Hz\n",
pll_in_freq);
}
pll_out_freq[0]=div64_u64(pll_in_freq_scaled*best_in_div,out_denom);
pll_out_freq[1]=(pll_in_freq_scaled*best_in_div)-pll_out_freq[0]*out_denom;
pll_out_freq[2]=out_denom;
dev_dbg(&client->dev, "PLL output frequency: %llu+%llu/%llu Hz, MS input divider: %lld, MS output divider: %lld\n",
pll_out_freq[0],pll_out_freq[1],pll_out_freq[2], best_in_div, out_div);
return set_pll_freq(client, pll_out_freq, 1); /* integer result */
}
}
static int get_pll_ms_freq(struct i2c_client *client, u64 *out_freq, int chn)
{
int rc;
u64 pll_out_freq[3], ms[3], pll_freq_scaled, ms_scaled;
u32 p123[3];
if (((rc=get_pll_freq(client,pll_out_freq)))<0) return rc;
/* trim PLL frequency fraction */
while (pll_out_freq[2]>=0x1000){
pll_out_freq[1] >>= 1;
pll_out_freq[2] >>= 1;
}
pll_freq_scaled=pll_out_freq[0]*pll_out_freq[2]+pll_out_freq[1];
if (((rc=get_ms_p123(client,p123, chn)))<0) return rc; /* includes invalid chn */
p123_to_ms(ms,p123);
/* trim MS divisor fraction */
while (ms[2]>=0x1000){
ms[1] >>= 1;
ms[2] >>= 1;
}
ms_scaled=ms[0]*ms[2]+ms[1];
out_freq[1]=pll_freq_scaled*ms[2];
out_freq[2]=ms_scaled*pll_out_freq[2];
if (out_freq[2]==0){
out_freq[0]=0;
out_freq[1]=0;
out_freq[2]=1;
} else {
out_freq[0]=div64_u64(out_freq[1],out_freq[2]);
out_freq[1]-=out_freq[0]*out_freq[2];
remove_common_factor(&out_freq[1]);
}
dev_dbg(&client->dev, "MS%d output frequency: %llu+%llu/%llu Hz\n",chn,out_freq[0],out_freq[1],out_freq[2]);
return 0;
}
/*
* Adjust MultiSynth divisor (MS0..MS3) for specified output frequency
* MSN, input frequency should be already set
* out_freq as int,num,denom
*/
static int set_pll_ms_by_out(struct i2c_client *client, u64 *out_freq, int chn, int int_div)
{
/* use r-divider if the output frequency is too low (less than 5 MHz) */
u64 out_int=out_freq[0],out_num=out_freq[1],out_denom=out_freq[2],
pll_out_freq[3],d;
s64 pll_freq_scaled, out_freq_scaled;
u64 ms[3];
u32 p123[3];
int r_div=1,rc;
if (out_denom==0){
dev_err(&client->dev, "denominator should not be 0 in %lld+%lld/%lld\n",
out_int,out_num,out_denom);
return -EINVAL;
}
if (out_num>=out_denom){ /* normalize */
d=div64_u64(out_num,out_denom);
out_int+=d;
out_num-=d*out_denom;
}
if (out_num==0){
out_denom=1;
}
if (out_int<(FVCOMAX/MSINT_MAX)){
while ((r_div < 32) && (out_int<(FVCOMAX/MSINT_MAX))){
out_int<<=1;
out_num<<=1;
r_div<<=1;
if (out_num>out_denom) {
out_int++;
out_num-=out_denom;
}
}
if (out_int<(FVCOMAX/MSINT_MAX)){
dev_err(&client->dev, "Specified output frequency is too low: %lld < %lld\n",
out_freq[0], FVCOMAX/MSINT_MAX/32);
return -EINVAL;
}
}
dev_dbg(&client->dev, "Output divider by %u, Output frequency before divider: %llu+%llu/%llu Hz\n",
r_div,out_int, out_num,out_denom);
/* trim output frequency fraction */
while (out_denom>=0x1000){
out_num >>= 1;
out_denom >>= 1;
}
out_freq_scaled=out_int*out_denom+out_num;
if (((rc=get_pll_freq(client,pll_out_freq)))<0) return rc;
/* trim PLL frequency fraction */
while (pll_out_freq[2]>=0x1000){
pll_out_freq[1] >>= 1;
pll_out_freq[2] >>= 1;
}
pll_freq_scaled=pll_out_freq[0]*pll_out_freq[2]+pll_out_freq[1];
ms[1]=pll_freq_scaled*out_denom;
ms[2]=out_freq_scaled*pll_out_freq[2];
ms[0]=div64_u64(ms[1],ms[2]);
ms[1]-=ms[0]*ms[2];
if (int_div){
if (ms[1]>(ms[2]>>1)) ms[0]++;
ms[1]=0;
ms[2]=1;
} else {
remove_common_factor(&ms[1]);
}
dev_dbg(&client->dev, "MS%d divider: %llu+%llu/%llu\n",chn,ms[0],ms[1],ms[2]);
/* set up registers */
ms_to_p123(ms,p123);
if (((rc=set_ms_p123(client,p123, chn)))<0) return rc;
if (((rc=disable_spread_spectrum(client,chn)))<0) return rc;
return 0;
}
/* ----------- Input section ----------------- */
static s64 get_pll_in_frequency(struct i2c_client *client)
{
int mux;
int div=1;
s64 freq;
if (((mux=get_in_pfd_ref_fb(client,0)))<0) return mux;
switch (mux){
case 0:
freq= get_p1div_in_frequency(client);
break;
case 1:
freq= get_p2div_in_frequency(client);
break;
case 2:
freq= get_p1div_in_frequency(client);
div=get_in_pdiv(client,0);
break;
case 3:
freq= get_p2div_in_frequency(client);
div=get_in_pdiv(client,1);
break;
case 4:
freq= get_in_frequency (client,0); /* IN1/IN2, xtal */
break;
case 5:
freq= 0; /* No clock */
break;
default:
dev_err(&client->dev, "Invalid value for PLL input multiplexer %d\n",mux);
return - EINVAL;
}
if (freq<0) return freq;
if (div<0) return div;
/* TODO - make it fractional? */
return div64_u64(freq,div);
}
static s64 get_pll_fb_frequency(struct i2c_client *client)
{
int mux;
int div=1;
s64 freq;
if (((mux=get_in_pfd_ref_fb(client,1)))<0) return mux;
switch (mux){
case 0:
freq= get_p2div_in_frequency(client);
break;
case 1:
freq= get_p1div_in_frequency(client);
break;
case 2:
freq= get_p2div_in_frequency(client);
div=get_in_pdiv(client,1);
break;
case 3:
freq= get_p1div_in_frequency(client);
div=get_in_pdiv(client,0);
break;
/* case 4: */
case 5:
freq= 0; /* No clock */
break;
default:
dev_err(&client->dev, "Invalid value for PLL feedback multiplexer %d\n",mux);
return - EINVAL;
}
if (freq<0) return freq;
if (div<0) return div;
/* TODO - make it fractional? */
return div64_u64(freq,div);
}
static s64 get_p1div_in_frequency(struct i2c_client *client)
{
int mux;
if (((mux= get_in_mux(client)))<0) return mux;
switch (mux){
case 0: return get_in_frequency (client,0); /* IN1/IN2 */
case 1: return get_in_frequency (client,1); /* IN3 */
case 2: return get_in_frequency (client,0); /* IN1/IN2 - xtal*/
default:
dev_err(&client->dev, "Invalid value for input multiplexer %d\n",mux);
return - EINVAL;
}
}
static s64 get_p2div_in_frequency(struct i2c_client *client)
{
int mux;
if (((mux= get_fb_mux(client)))<0) return mux;
switch (mux){
case 0: return get_in_frequency (client,3); /* IN5/IN6 */
case 1: return get_in_frequency (client,1); /* IN4 */
case 2: return 0; /* no clock */
default:
dev_err(&client->dev, "Invalid value for input multiplexer %d\n",mux);
return - EINVAL;
}
}
static int set_in_mux(struct i2c_client *client, int data)
{
int data1,rc;
switch (data) {
case 0: data1=0; break;
case 1: data1=2; break;
case 2: data1=5; break;
default:
dev_err(&client->dev, "Invalid value for input multiplexer %d\n",data);
return - EINVAL;
}
if (((rc=write_field (client, data, AWE_IN_MUX )))<0) return rc;
if (((rc=write_field (client, data1, AWE_IN_MUX1)))<0) return rc;
return 0;
}
static int get_in_mux(struct i2c_client *client)
{
return read_field(client,AWE_IN_MUX );
}
static int set_fb_mux(struct i2c_client *client, int data)
{
int data1,rc;
switch (data) {
case 0: data1=0; break;
case 1: data1=1; break;
case 2: data1=0; break;
default:
dev_err(&client->dev, "Invalid value for feedback multiplexer %d\n",data);
return - EINVAL;
}
if (((rc=write_field (client, data, AWE_FB_MUX )))<0) return rc;
if (((rc=write_field (client, data1, AWE_FB_MUX1)))<0) return rc;
return 0;
}
static int get_fb_mux(struct i2c_client *client)
{
return read_field(client,AWE_FB_MUX );
}
static const u8 in_div_values[]={1,2,4,8,16,32};
static int set_in_pdiv(struct i2c_client *client, int div, int chn) /*chn =0,1 */
{
int rc;
u8 val;
for (val=0;valdev, "Invalid value for input divider: %d\n",div);
return - EINVAL;
}
static int get_in_pdiv(struct i2c_client *client, int chn) /*chn =0,1 */
{
int rc;
if (((rc=read_field(client, chn?AWE_P2DIV:AWE_P1DIV )))<0) return rc;
if (rc>=ARRAY_SIZE(in_div_values)){
dev_err(&client->dev, "Invalid value for input divider: %d\n",rc);
return - EINVAL;
}
return in_div_values[rc];
}
static int set_in_pfd_ref_fb(struct i2c_client *client, u8 val, int chn) /*chn =0 - ref, 1 - fb*/
{
int rc;
if (val>5) {
dev_err(&client->dev, "Invalid value for input pfd selector: %d\n", (int) val);
return - EINVAL;
}
if (((rc=write_field (client, val, chn?AWE_PFD_FB:AWE_PFD_REF )))<0) return rc;
return 0;
}
static int get_in_pfd_ref_fb(struct i2c_client *client, int chn) /*chn =0,1 */
{
return read_field(client, chn?AWE_PFD_FB:AWE_PFD_REF );
}
static int set_fb_external(struct i2c_client *client, u8 val)
{
int rc;
if (((rc=write_field (client, val?1:0, AWE_PFD_EXTFB)))<0) return rc;
return 0;
}
static int get_fb_external(struct i2c_client *client)
{
return read_field(client,AWE_PFD_EXTFB);
}
static int set_in_frequency(struct i2c_client *client, u64 frequency,int src) /* 0 - 12, 1 - 3, 2 - 4, 3 - 5,6, 4 - 12XO */
{
int xtal_mode,rc,idiv;
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
if (frequency < INFREQMIN){
dev_err(&client->dev, "Input frequency too low: %llu < %llu\n",frequency, INFREQMIN);
return - EINVAL;
}
if (frequency > INFREQMAX){
dev_err(&client->dev, "Input frequency too high: %llu > %llu\n",frequency, INFREQMAX);
return - EINVAL;
}
for (idiv=0;idiv<5;idiv++) if ((frequency >> idiv) <= INFREQDIV) break;
switch (src){
case 4: /* set xtal mode */
xtal_mode=0;
if (frequency>11000000ll) xtal_mode=1;
if (frequency>19000000ll) xtal_mode=2;
if (frequency>26000000ll) xtal_mode=3;
if (((rc=write_field (client, xtal_mode, AWE_XTAL_FREQ)))<0) return rc;
if (((rc=set_in_mux(client, 2)))<0) return rc; /* in mux to XO */
if (((rc=set_in_pfd_ref_fb(client, 4, 0)))<0) return rc; /* set pfd reference to XO - may use 0 (p1div_in also? )*/
clientdata->input_frequency12=frequency;
break;
case 0:
if (((rc=set_in_mux(client, 0)))<0) return rc; /* in mux to IN12 */
if (idiv==0){
if (((rc=set_in_pfd_ref_fb(client, 0, 0)))<0) return rc; /* set pfd reference to p1div_in */
} else {
if (((rc=set_in_pfd_ref_fb(client, 2, 0)))<0) return rc; /* set pfd reference to p1div_out */
if (((rc=set_in_pdiv(client, 1<input_frequency12=frequency;
break;
case 1:
if (((rc=set_in_mux(client, 1)))<0) return rc; /* in mux to IN3 */
if (idiv==0){
if (((rc=set_in_pfd_ref_fb(client, 0, 0)))<0) return rc; /* set pfd reference to p1div_in */
} else {
if (((rc=set_in_pfd_ref_fb(client, 2, 0)))<0) return rc; /* set pfd reference to p1div_out */
if (((rc=set_in_pdiv(client, 1<input_frequency3=frequency;
break;
case 2:
if (((rc=set_fb_mux(client, 1)))<0) return rc; /* fb mux to IN4 */
if (idiv==0){
if (((rc=set_in_pfd_ref_fb(client, 1, 0)))<0) return rc; /* set pfd reference to p2div_in */
} else {
if (((rc=set_in_pfd_ref_fb(client, 3, 0)))<0) return rc; /* set pfd reference to p2div_out */
if (((rc=set_in_pdiv(client, 1<input_frequency4=frequency;
break;
case 3:
if (((rc=set_fb_mux(client, 0)))<0) return rc; /* fb mux to IN5/6 */
if (idiv==0){
if (((rc=set_in_pfd_ref_fb(client, 1, 0)))<0) return rc; /* set pfd reference to p2div_in */
} else {
if (((rc=set_in_pfd_ref_fb(client, 3, 0)))<0) return rc; /* set pfd reference to p2div_out */
if (((rc=set_in_pdiv(client, 1<input_frequency56=frequency;
break;
default:
dev_err(&client->dev, "Invalid source %d only 0 (IN1/2), 1 (IN3), 2 (IN4), 3 (IN4/IN5) and 4 (IN1/2, XO) are supported\n",src);
return - EINVAL;
}
return 0;
}
static u64 get_in_frequency(struct i2c_client *client, int src)
{
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
switch (src){
case 0: return clientdata->input_frequency12;
case 1: return clientdata->input_frequency3;
case 2: return clientdata->input_frequency4;
case 3: return clientdata->input_frequency56;
default:
dev_err(&client->dev, "Invalid source %d only 0 (IN1/2), 1 (IN3), 2 (IN4) and 3 (IN4/IN5) are supported\n",src);
return - EINVAL;
}
}
/* ----------- General ----------------- */
static s64 read_multireg64 (struct i2c_client *client, const u32 * awe)
{
int i,nshift,nbits, full_shift=0;
u8 mask;
u16 reg;
s64 data=0, rc;
for (i=0;awe[i]!=0;i++){
reg=awe[i]>>8;
mask=awe[i]&0xff;
if (mask!=0){
nshift=0;
nbits=1;
while (((1<>= nshift;
rc <<= full_shift;
data |= rc;
full_shift+=nbits;
}
}
return data;
}
static int write_multireg64 (struct i2c_client *client, u64 data, const u32 * awe)
{
int i,rc,nshift,nbits;
u8 mask,reg_data;
u16 reg;
for (i=0;awe[i]!=0;i++){
reg=awe[i]>>8;
mask=awe[i]&0xff;
if (mask!=0){
nshift=0;
nbits=1;
while (((1<>= nbits;
if (((rc=write_reg(client, reg, reg_data, mask)))<0) return rc;
}
}
return 0;
}
static int read_field (struct i2c_client *client, u32 awe)
{
int rc,nshift;
u8 mask;
u16 reg;
reg=awe>>8;
mask=awe&0xff;
if (mask!=0){
nshift=0;
while (((1<> nshift;
}
return 0;
}
static int write_field (struct i2c_client *client, u8 data, u32 awe)
{
int rc,nshift;
u8 mask,reg_data;
u16 reg;
reg=awe>>8;
mask=awe&0xff;
if (mask!=0){
nshift=0;
while (((1<>8) & 0xff;
u16 reg= (adwe>>16) & (0xff | (REG5338_PAGE_MASK << 8)); /* 0x1ff */
return write_reg(client, reg, data, we);
}
static int _write_single_reg(struct i2c_client *client, u8 reg, u8 val)
{
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
int ireg=reg;
dev_dbg(&client->dev,"device write: slave=0x%x, reg=0x%x, val=0x%x\n", (int) (client->addr),reg,val);
if (clientdata) {
if (reg==REG5338_PAGE) {
// dev_dbg(&client->dev,"changing page: new=0x%x, was=0x%x\n",val & REG5338_PAGE_MASK,clientdata->last_page);
clientdata->last_page=val & REG5338_PAGE_MASK;
} else {
ireg |=(clientdata->last_page)<<8;
}
if (ireg<=LAST_REG){
clientdata->cache[ireg].data= val;
clientdata->cache[ireg].flags |= CACHE_INIT;
}
}
return i2c_smbus_write_byte_data(client, reg, val);
}
static int write_reg(struct i2c_client *client, u16 reg, u8 val, u8 mask)
{
int rc,page;
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
if (mask==0) return 0;
dev_dbg(&client->dev,"reg=0x%x, val=0x%x, mask=0x%x\n", (int) reg, (int) val, (int) mask);
if (mask !=0xff){
if (((rc=read_reg(client, reg)))<0) return rc; /* full register including page */
val=((val ^ rc) & mask)^ rc;
if ((val==rc) && !(clientdata->cache[reg].flags & CACHE_VOLAT)) {
dev_dbg(&client->dev,"No change and not volatile -> no write\n");
return 0;
}
}
page=(reg >> 8) & REG5338_PAGE_MASK;
if (page != (clientdata->last_page)) { /* set page if needed */
if (((rc=_write_single_reg(client, REG5338_PAGE, page)))<0) return rc;
}
return _write_single_reg(client, reg & 0xff, val);
}
static int read_reg(struct i2c_client *client, u16 reg)
{
int rc,page;
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
if (clientdata && (reg<=LAST_REG) && (clientdata->cache[reg].flags & CACHE_INIT) && !(clientdata->cache[reg].flags & CACHE_VOLAT)){
dev_dbg(&client->dev,"Using cached register: reg=0x%x -> 0x%x\n",reg,(int) clientdata->cache[reg].data);
return clientdata->cache[reg].data;
}
page=(reg >> 8) & REG5338_PAGE_MASK;
// dev_dbg(&client->dev,"reading i2c device : slave=0x%x, reg=0x%x page=0x%x, last_page=0x%x\n",(int) (client->addr),reg,page,clientdata->last_page);
if (clientdata && (reg!=REG5338_PAGE) && (page != clientdata->last_page)) { /* set page if needed */
if (((rc=_write_single_reg(client, REG5338_PAGE, page)))<0) return rc;
}
rc= i2c_smbus_read_byte_data(client, reg & 0xff);
dev_dbg(&client->dev,"reading i2c device : slave=0x%x, reg=0x%x -> 0x%x\n",(int) (client->addr),reg,rc);
if (rc<0) return rc;
if (clientdata && (reg==REG5338_PAGE)) {
clientdata->last_page= rc & REG5338_PAGE_MASK;
}
if (clientdata && (reg<=LAST_REG)){
clientdata->cache[reg].data= (u8) rc;
clientdata->cache[reg].flags |= CACHE_INIT;
}
return rc;
}
static void si5338_init_of(struct i2c_client *client)
{
// struct device *dev=&client->dev;
const __be32 * config_data;
const char * init_type_string;
int init_type=0; /* 0 - none, 1 - always, 2 - if not running (TODO) */
struct device_node *node = client->dev.of_node;
int len,i,n;
u16 page_reg;
char buf[40];
u64 freq[3];
u32 rate,amp;
struct si5338_setup_data {
u8 page;
u8 reg;
u8 data;
u8 mask;
};
struct si5338_setup_data setup_data;
__be32 * setup_data_be32= (__be32 *) &setup_data;
if (node) {
init_type_string = of_get_property(client->dev.of_node, "si5338,init", &len);
if (init_type_string){
if (strcmp(init_type_string,"always")==0) init_type=1;
else if (strcmp(init_type_string,"if off")==0) init_type=2;
else {
dev_err(&client->dev,"Unrecognized si5338 initialization type '%s'. Only 'always' and 'if off' are permitted\n",init_type_string);
}
}
switch (init_type){
case 2:
// static int is_set_up(struct i2c_client *client);
i=is_set_up(client);
if (i<0){
dev_err(&client->dev,"Error reading i2c register, aborting initialization\n");
return;
} else if (i>0){
init_type=0;
dev_dbg(&client->dev,"Skipping conditional initialization (some driver variables will not be initialized)\n");
return;
}
init_type=1;
/* falling to initialization */
case 1:
pre_init(client,1); // clear outputs and muxes - they will be programmed later
break;
}
config_data = of_get_property(client->dev.of_node, "si5338,configuration_data", &len);
if (config_data){
len /= sizeof(*config_data);
dev_dbg(&client->dev,"Read %d values\n",len);
dev_dbg(&client->dev,"Found %d items in 'si5338,configuration_data' in the Device Tree\n",len);
for (i=0;idev,"page_reg=0x%03x, data=0x%02x, mask=0x%02x \n",
(int) page_reg,(int)setup_data.data,(int)setup_data.mask);
if (write_reg(client, page_reg, setup_data.data, setup_data.mask)<0) return;
}
}
/* input section */
/* setting input frequency here divides (if needed) and feeds it to the PLL reference. Other variants can use raw register writes */
for (n=0;in_freq_names[n];n++){
sprintf(buf,"si5338,%s",in_freq_names[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data && (len>0)){
dev_dbg(&client->dev,"Found '%s', value = %d (0x%x)\n",buf,(int)(be32_to_cpup(config_data)),(int)(be32_to_cpup(config_data)));
if (set_in_frequency(client, be32_to_cpup(config_data),n)<0) return; /* 32 bits are sufficient here */
}
}
/* setting PLL for the most important output frequency, sets analog parameters accordingly. Assumes input frequency set above */
for (n=0;pll_setup_names[n];n++){
sprintf(buf,"si5338,%s",pll_setup_names[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data && (len>0)){
len /= sizeof(*config_data);
freq[0]=be32_to_cpup(config_data);
if (len<3){
freq[1]=0;
freq[2]=1;
} else {
freq[1]=be32_to_cpup(&config_data[1]);
freq[2]=be32_to_cpup(&config_data[2]);
}
dev_dbg(&client->dev,"Found '%s', value = %lld+(%lld/%lld)\n",buf,freq[0],freq[1],freq[2]);
if (n & 2){ /* by output */
if (set_pll_freq_by_out(client, freq, n & 1)<0) return;
} else { /* directly set PLL frequency */
if (set_pll_freq (client, freq, n & 1)<0) return;
}
if (set_pll_paremeters(client)<0) return;
/* if (set_misc_registers(client)<0) return; */ /* moved to pre_init() */
}
}
/* setting MSn dividers (same channel as output), powering them up, setting output dividers and routing outputs */
for (n=0;out_freq_setup_names[n];n++){
sprintf(buf,"si5338,%s",out_freq_setup_names[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data && (len>0)){
len /= sizeof(*config_data);
freq[0]=be32_to_cpup(config_data);
if (len<3){
freq[1]=0;
freq[2]=1;
} else {
freq[1]=be32_to_cpup(&config_data[1]);
freq[2]=be32_to_cpup(&config_data[2]);
}
dev_dbg(&client->dev,"Found '%s', value = %lld+(%lld/%lld)\n",buf,freq[0],freq[1],freq[2]);
if (set_out_frequency_and_route(client, freq, n&3, n>>2)<0) return;
}
}
/* configure output driver standard */
for (n=0;drv_configs[n].description;n++){
sprintf(buf,"si5338,%s",drv_configs[n].description);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data){
len /= sizeof(*config_data);
for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
if (configure_output_driver(&client->dev, drv_configs[n].description, setup_data.mask)<0) return;
}
}
}
/* configure disabled state of the output(s) */
for (n=0;out_dis_states[n];n++){
sprintf(buf,"si5338,%s",out_dis_states[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data){
len /= sizeof(*config_data);
for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
if (set_drv_disable(client, n, setup_data.mask)<0) return;
}
}
}
/* configure powerdown state of the output(s) */
for (n=0;out_pwr_states[n];n++){
sprintf(buf,"si5338,%s",out_pwr_states[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data){
len /= sizeof(*config_data);
for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
if (set_drv_powerdown(client, n, setup_data.mask)<0) return;
}
}
}
/* configure output enable state of the output(s) */
for (n=0;out_en_states[n];n++){
sprintf(buf,"si5338,%s",out_en_states[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data){
len /= sizeof(*config_data);
for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
if (set_drv_disable(client, n, setup_data.mask)<0) return;
}
}
}
/* setting spread spectrum parameters */
for (n=0;n<4;n++){
sprintf(buf,"si5338,spread_spectrum_%d",n);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data && (len>0)){
len /= sizeof(*config_data);
rate=get_ss_down_rate(client, n);
amp= get_ss_down_amplitude(client, n);
if (len>1) amp = be32_to_cpup(&config_data[1]);
if (len>2) rate = be32_to_cpup(&config_data[2]);
if (store_ss_down_parameters(client, rate, amp, n)==0){
dev_dbg(&client->dev,"Set spread spectrum parameters for MS%d, amplitude=%d (*0.01%%), rate=%d Hz, %s\n",
n,amp,rate,config_data[0]?"ON":"OFF");
} else {
dev_err(&client->dev,"Failed to set spread spectrum parameters for MS%d, amplitude=%d (*0.01%%), rate=%d Hz, %s\n",
n,amp,rate,config_data[0]?"ON":"OFF");
continue;
}
if (config_data[0]){ /* enable SS */
if ((set_ss_down(client, n)==0) && /* calculate and set SS registers */
(set_ss_state(client, 1, n)==0)){ // enable SS. Not using enable_spread_spectrum() as we'll reset MS later anyway
dev_dbg(&client->dev,"Spread spectrum enabled for MS%d\n",n);
} else {
dev_err(&client->dev,"Fail to enable spread spectrum for MS%d\n",n);
}
}
}
}
} else {
dev_info(&client->dev,"Device tree data not found for %s\n",client->name);
}
if (init_type){
if (post_init(client,INIT_TIMEOUT)<0) dev_err(&client->dev,"SI5338 initialization failed\n");
else dev_info(&client->dev,"SI5338 initialized\n");
}
}
static void invalidate_cache(struct i2c_client *client)
{
int i;
struct si5338_data_t *clientdata = i2c_get_clientdata(client);
for (i=0;i<=LAST_REG;i++){
clientdata->cache[i].flags&= ~CACHE_INIT;
}
}
static int si5338_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int i,rc=0;
struct si5338_data_t *clientdata = NULL;
/* initialize i2c ... */
//#define REG5338_DEV_CONFIG2 2
//#define REG5338_DEV_CONFIG2_MASK 0x3f
//#define REG5338_DEV_CONFIG2_VAL 38 /* last 2 digits of part number */
if (((rc=_write_single_reg(client, REG5338_PAGE,0)))<0) return rc; // did not respond
if (((rc=read_reg(client, REG5338_DEV_CONFIG2)))<0) return rc; // did not respond
if ((rc & REG5338_DEV_CONFIG2_MASK)!= REG5338_DEV_CONFIG2_VAL){
dev_err(&client->dev,
"Chip returned unexpected value from reg %d: %d, expected %d. It is not %s\n",
REG5338_DEV_CONFIG2,rc, REG5338_DEV_CONFIG2_VAL,id->name);
return -EIO;
}
dev_info(&client->dev,
"Chip %s is found, driver version %s\n", id->name, DRV_VERSION);
clientdata = devm_kzalloc(&client->dev, sizeof(*clientdata), GFP_KERNEL);
for (i=0;i<=LAST_REG;i++){
clientdata->cache[i].flags=0;
clientdata->cache[i].data=0;
}
for (i=0;volatile_registers[i]>=0;i++){
clientdata->cache[volatile_registers[i]>>8].flags |= CACHE_VOLAT;
}
//volatile_registers[]
i2c_set_clientdata(client, clientdata);
if (((rc=read_reg(client, REG5338_PAGE)))<0) return rc; // will set clientdata->last_page
si5338_sysfs_register(&client->dev);
mutex_init(&clientdata->lock);
clientdata->input_frequency12=0;
clientdata->input_frequency3=0;
clientdata->input_frequency4=0;
clientdata->input_frequency56=0;
clientdata->ss_on_freq_change=0; /* 0 - disable SS when frequency is changed, 1 - update SS. +2 reset MS after starting SS*/
for (i=0;i<4;i++){
clientdata->spread_spectrum_rate[i]=SPREAD_RATE_DFLT; /* 31.5 KHz */
clientdata->spread_spectrum_amp[i]=SPREAD_AMP_DFLT; /* 0.5% */
}
si5338_init_of(client);
return 0;
}
static int si5338_i2c_remove(struct i2c_client *client)
{
return 0;
}
static struct i2c_driver si5338_i2c_driver = {
.driver = {
.name = "si5338",
.owner = THIS_MODULE,
},
.probe = si5338_i2c_probe,
.remove = si5338_i2c_remove,
.id_table = si5338_id,
};
module_i2c_driver(si5338_i2c_driver);
MODULE_DEVICE_TABLE(i2c, si5338_id);
MODULE_AUTHOR("Andrey Filippov ");
MODULE_DESCRIPTION("SI5338 I2C bus driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("i2c:si5338");
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/elphel/ 0000775 0000000 0000000 00000000000 12666203034 0024715 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/elphel/Kconfig 0000664 0000000 0000000 00000001722 12666203034 0026222 0 ustar 00root root 0000000 0000000 #
# Elphel devices
#
menu "Elphel devices"
config ELPHEL393
tristate "Support Elphel 10393 board voltage regulator, DMA memory allocator"
# add more
# depends on I2C && SYSFS
help
Say Y here if you have a Elphel board 10393.
config ELPHELDRVONMICROZED
tristate "Provide only Elphel features which are compatible with Microzed"
help
Say Y here if you debug Elpel camera code on Microzed board.
endmenu
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/elphel/Makefile 0000664 0000000 0000000 00000001302 12666203034 0026351 0 ustar 00root root 0000000 0000000 #
# Makefile for Elphel specific devices.
#
obj-$(CONFIG_ELPHEL393) += elphel393-pwr.o
obj-$(CONFIG_ELPHEL393) += elphel393-mem.o
obj-$(CONFIG_ELPHELDRVONMICROZED) += elphel393-mem.o
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/elphel/elphel393-mem.c 0000664 0000000 0000000 00000041123 12666203034 0027346 0 ustar 00root root 0000000 0000000 /*!***************************************************************************
*! FILE NAME : elphel393-mem.c
*! DESCRIPTION: Reserve large memory range at boot time (when it is available)
*! to use as a circular video buffer
*! Copyright (C) 2015 Elphel, 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 3 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 .
*!****************************************************************************/
#include
#include
#include
#include
#include // kmalloc
#include // vmalloc
#include // __get_free_pages
#include
#include
#include
#include
#include
#include
#include
#include
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
static ssize_t get_paddr(struct device *dev, struct device_attribute *attr, char *buf);
static struct elphel_buf_t _elphel_buf = {
// Coherent DMA buffer
.vaddr = NULL,
.paddr = 0,
.size = 0,
// Host to device stream DMA buffer
.h2d_vaddr = NULL,
.h2d_paddr = 0,
.h2d_size = 1024, // TODO: add to DT, learn how to allocate more
// Device to host stream DMA buffer
.d2h_vaddr = NULL,
.d2h_paddr = 0,
.d2h_size = 1024,
// Bidirectional stream DMA buffer
.bidir_vaddr = NULL,
.bidir_paddr = 0,
.bidir_size = 1024
};
struct elphel_buf_t *pElphel_buf; // static can not be extern
EXPORT_SYMBOL_GPL(pElphel_buf);
static int __init elphelmem_init(void)
{
struct device_node *node;
const __be32 *bufsize_be;
pElphel_buf = &_elphel_buf;
node = of_find_node_by_name(NULL, "elphel393-mem");
if (!node)
{
printk("DMA buffer allocation ERROR: No device tree node found\n");
return -ENODEV;
}
bufsize_be = (__be32 *)of_get_property(node, "memsize", NULL);
_elphel_buf.size = be32_to_cpup(bufsize_be);
/*
// Coherent DMA buffer
void *vaddr;
dma_addr_t paddr;
ssize_t size;
// Host to device stream DMA buffer
void *h2d_vaddr;
dma_addr_t h2d_paddr;
ssize_t h2d_size;
// Device to host stream DMA buffer
void *d2h_vaddr;
dma_addr_t d2h_paddr;
ssize_t d2h_size;
// Bidirectional stream DMA buffer
void *bidir_vaddr;
dma_addr_t bidir_paddr;
ssize_t bidir_size;
_elphel_buf.vaddr = dma_alloc_coherent(NULL,(_elphel_buf.size*PAGE_SIZE),&(_elphel_buf.paddr),GFP_KERNEL);
if(_elphel_buf.paddr)
{
printk("Allocated %u pages for DMA at address 0x%x\n", (u32)_elphel_buf.size, (u32)_elphel_buf.paddr);
}
else printk("ERROR allocating memory buffer");
http://linuxkernelhacker.blogspot.com/2014/07/arm-dma-mapping-explained.html
*/
// Alternative way to allocate memory for DMA
// allocate continuous virtual memory range
// _elphel_buf.vaddr = kmalloc((_elphel_buf.size*PAGE_SIZE) ,GFP_KERNEL);
_elphel_buf.vaddr = dma_alloc_coherent(NULL,(_elphel_buf.size*PAGE_SIZE),&(_elphel_buf.paddr),GFP_KERNEL);
if(_elphel_buf.paddr) {
printk("Allocated %u pages for DMA at address 0x%x\n", (u32)_elphel_buf.size, (u32)_elphel_buf.paddr);
} else {
printk("ERROR allocating coherent DMA memory buffer");
}
_elphel_buf.h2d_vaddr = kzalloc((_elphel_buf.h2d_size*PAGE_SIZE) ,GFP_KERNEL);
if (!_elphel_buf.h2d_vaddr){
_elphel_buf.h2d_size = 0;
printk("ERROR allocating H2D DMA memory buffer");
}
_elphel_buf.d2h_vaddr = kzalloc((_elphel_buf.d2h_size*PAGE_SIZE) ,GFP_KERNEL);
if (!_elphel_buf.d2h_vaddr){
_elphel_buf.d2h_size = 0;
printk("ERROR allocating D2H DMA memory buffer");
}
_elphel_buf.bidir_vaddr = kzalloc((_elphel_buf.bidir_size*PAGE_SIZE) ,GFP_KERNEL);
if (!_elphel_buf.bidir_vaddr){
_elphel_buf.bidir_size = 0;
printk("ERROR allocating Bidirectional DMA memory buffer");
}
printk("Coherent buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> vaddr);
printk("Coherent buffer paddr: 0x%08X\n",(u32) pElphel_buf -> paddr);
printk("Coherent buffer length: 0x%08X\n",(u32) pElphel_buf -> size * PAGE_SIZE);
return 0;
}
/*
dma_addr_t
dma_map_single(struct device *dev, void *cpu_addr, size_t size,
enum dma_data_direction direction)
*/
static void __exit elphelmem_exit(void)
{
printk("DMA buffer disabled\n");
}
// SYSFS
static ssize_t get_paddr(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.paddr);
}
static ssize_t get_size(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%u\n", _elphel_buf.size);
}
static ssize_t get_paddr_h2d(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.h2d_paddr);
}
static ssize_t get_size_h2d(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%u\n", _elphel_buf.h2d_size);
}
static ssize_t get_paddr_d2h(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.d2h_paddr);
}
static ssize_t get_size_d2h(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%u\n", _elphel_buf.d2h_size);
}
static ssize_t get_paddr_bidir(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.bidir_paddr);
}
static ssize_t get_size_bidir(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%u\n", _elphel_buf.bidir_size);
}
/*
static ssize_t get_cache(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write into this file to flush L1/L2 caches to memory.\n");
}
static ssize_t flush_cache(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
__cpuc_flush_kern_all();
outer_flush_all();
return count;
}
*/
static ssize_t sync_for_cpu_h2d(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.h2d_paddr;
len = _elphel_buf.h2d_size * PAGE_SIZE;
}
printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_cpu(dev, paddr, len, DMA_TO_DEVICE);
return count;
}
static ssize_t sync_for_device_h2d(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.h2d_paddr;
len = _elphel_buf.h2d_size * PAGE_SIZE;
}
printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_device(dev, paddr, len, DMA_TO_DEVICE);
return count;
}
static ssize_t sync_for_cpu_d2h(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.d2h_paddr;
len = _elphel_buf.d2h_size * PAGE_SIZE;
}
printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_cpu(dev, paddr, len, DMA_FROM_DEVICE);
return count;
}
static ssize_t sync_for_device_d2h(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.d2h_paddr;
len = _elphel_buf.d2h_size * PAGE_SIZE;
}
printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_device(dev, paddr, len, DMA_FROM_DEVICE);
return count;
}
static ssize_t sync_for_cpu_bidir(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.bidir_paddr;
len = _elphel_buf.bidir_size * PAGE_SIZE;
}
printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_cpu(dev, paddr, len, DMA_BIDIRECTIONAL);
return count;
}
static ssize_t sync_for_device_bidir(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.bidir_paddr;
len = _elphel_buf.bidir_size * PAGE_SIZE;
}
printk("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_device(dev, paddr, len, DMA_BIDIRECTIONAL);
return count;
}
static ssize_t get_sync_for_device_h2d(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the host to device DMA buffer to device (after CPU writes).\n");
}
static ssize_t get_sync_for_cpu_h2d(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the host to device DMA buffer to CPU (before CPU reads).\n");
}
static ssize_t get_sync_for_device_d2h(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the device to host DMA buffer to device (after CPU writes).\n");
}
static ssize_t get_sync_for_cpu_d2h(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the device to host DMA buffer to CPU (before CPU reads).\n");
}
static ssize_t get_sync_for_device_bidir(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the bidirectional DMA buffer to device (after CPU writes).\n");
}
static ssize_t get_sync_for_cpu_bidir(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the bidirectional DMA buffer to CPU (before CPU reads).\n");
}
static DEVICE_ATTR(buffer_address, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr, NULL);
static DEVICE_ATTR(buffer_pages, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size, NULL);
static DEVICE_ATTR(buffer_address_h2d, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_h2d, NULL);
static DEVICE_ATTR(buffer_pages_h2d, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_h2d, NULL);
static DEVICE_ATTR(buffer_address_d2h, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_d2h, NULL);
static DEVICE_ATTR(buffer_pages_d2h, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_d2h, NULL);
static DEVICE_ATTR(buffer_address_bidir, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_bidir, NULL);
static DEVICE_ATTR(buffer_pages_bidir, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_bidir, NULL);
//static DEVICE_ATTR(buffer_flush, SYSFS_PERMISSIONS, get_cache, flush_cache);
static DEVICE_ATTR(sync_for_cpu_h2d, SYSFS_PERMISSIONS, get_sync_for_cpu_h2d, sync_for_cpu_h2d);
static DEVICE_ATTR(sync_for_device_h2d, SYSFS_PERMISSIONS, get_sync_for_device_h2d, sync_for_device_h2d);
static DEVICE_ATTR(sync_for_cpu_d2h, SYSFS_PERMISSIONS, get_sync_for_cpu_d2h, sync_for_cpu_d2h);
static DEVICE_ATTR(sync_for_device_d2h, SYSFS_PERMISSIONS, get_sync_for_device_d2h, sync_for_device_d2h);
static DEVICE_ATTR(sync_for_cpu_bidir, SYSFS_PERMISSIONS, get_sync_for_cpu_bidir, sync_for_cpu_bidir);
static DEVICE_ATTR(sync_for_device_bidir, SYSFS_PERMISSIONS, get_sync_for_device_bidir, sync_for_device_bidir);
static struct attribute *root_dev_attrs[] = {
&dev_attr_buffer_address.attr,
&dev_attr_buffer_pages.attr,
&dev_attr_buffer_address_h2d.attr,
&dev_attr_buffer_pages_h2d.attr,
&dev_attr_buffer_address_d2h.attr,
&dev_attr_buffer_pages_d2h.attr,
&dev_attr_buffer_address_bidir.attr,
&dev_attr_buffer_pages_bidir.attr,
// &dev_attr_buffer_flush.attr,
&dev_attr_sync_for_cpu_h2d.attr,
&dev_attr_sync_for_device_h2d.attr,
&dev_attr_sync_for_cpu_d2h.attr,
&dev_attr_sync_for_device_d2h.attr,
&dev_attr_sync_for_cpu_bidir.attr,
&dev_attr_sync_for_device_bidir.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static int elphel393_mem_sysfs_register(struct platform_device *pdev)
{
int retval=0;
struct device *dev = &pdev->dev;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
}
return retval;
}
static int elphel393_mem_probe(struct platform_device *pdev)
{
elphel393_mem_sysfs_register(pdev);
dev_info(&pdev->dev,"Probing elphel393-mem\n");
if (_elphel_buf.h2d_vaddr){
// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
pElphel_buf->h2d_paddr = dma_map_single(&pdev->dev, _elphel_buf.h2d_vaddr, (_elphel_buf.h2d_size*PAGE_SIZE), DMA_TO_DEVICE);
if (!pElphel_buf->h2d_paddr){
printk("ERROR in dma_map_single() for bidirectional buffer");
return 0;
}
// printk("H2D DMA buffer location:\t\t0x%08X\n", pElphel_buf->h2d_paddr);
}
if (_elphel_buf.d2h_vaddr){
// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
pElphel_buf->d2h_paddr = dma_map_single(&pdev->dev, _elphel_buf.d2h_vaddr, (_elphel_buf.d2h_size*PAGE_SIZE), DMA_FROM_DEVICE);
if (!pElphel_buf->d2h_paddr){
printk("ERROR in dma_map_single() for bidirectional buffer");
return 0;
}
// printk("D2H DMA buffer location:\t\t0x%08X\n", pElphel_buf->d2h_paddr);
}
if (_elphel_buf.bidir_vaddr){
// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
pElphel_buf->bidir_paddr = dma_map_single(&pdev->dev, _elphel_buf.bidir_vaddr, (_elphel_buf.bidir_size*PAGE_SIZE), DMA_BIDIRECTIONAL);
if (!pElphel_buf->bidir_paddr){
printk("ERROR in dma_map_single() for bidirectional buffer");
return 0;
}
// printk("Bidirectional DMA buffer location:\t0x%08X\n", pElphel_buf->bidir_paddr);
}
printk("H2D stream buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> h2d_vaddr);
printk("H2D stream buffer paddr: 0x%08X\n",(u32) pElphel_buf -> h2d_paddr);
printk("H2D stream buffer length: 0x%08X\n",(u32) pElphel_buf -> h2d_size * PAGE_SIZE);
printk("D2H stream buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> d2h_vaddr);
printk("D2H stream buffer paddr: 0x%08X\n",(u32) pElphel_buf -> d2h_paddr);
printk("D2H stream buffer length: 0x%08X\n",(u32) pElphel_buf -> d2h_size * PAGE_SIZE);
printk("Bidirectional stream buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> bidir_vaddr);
printk("Bidirectional stream buffer paddr: 0x%08X\n",(u32) pElphel_buf -> bidir_paddr);
printk("Bidirectional stream buffer length: 0x%08X\n",(u32) pElphel_buf -> bidir_size * PAGE_SIZE);
return 0;
}
static int elphel393_mem_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev,"Removing elphel393-mem");
return 0;
}
static struct of_device_id elphel393_mem_of_match[] = {
{ .compatible = "elphel,elphel393-mem-1.00", },
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, elphel393_pwr_of_match);
static struct platform_driver elphel393_mem = {
.probe = elphel393_mem_probe,
.remove = elphel393_mem_remove,
.driver = {
.name = "elphel393-mem",
.owner = THIS_MODULE,
.of_match_table = elphel393_mem_of_match,
.pm = NULL, /* power management */
},
};
module_platform_driver(elphel393_mem);
module_init(elphelmem_init);
module_exit(elphelmem_exit);
MODULE_LICENSE("GPL");
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/elphel/elphel393-pwr.c 0000664 0000000 0000000 00000130717 12666203034 0027410 0 ustar 00root root 0000000 0000000 /*!***************************************************************************
*! FILE NAME : elphel393-pwr.c
*! DESCRIPTION: power supplies control on Elphel 10393 board
*! Copyright (C) 2013 Elphel, 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 3 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 .
*/
#undef DEBUG /* should be before linux/module.h - enables dev_dbg at boot in this file */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DRIVER_DESCRIPTION "Elphel 10393 power supply control"
#define DRIVER_VERSION "1.00"
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
#define GPIO_CHIP1_ADDR 0x20
#define GPIO_CHIP2_ADDR 0x21
#define LTC3589_ADDR 0x34
/* TODO: set resistors in device tree to accommodate different revisions ( elphel393_pwr,vp15_r1 = <357000>)*/
#define VP15_R1 357000
#define VP15_R2 287000
#define VCC_SENS01_R1 787000
#define VCC_SENS01_R2 287000
#define VCC_SENS23_R1 787000
#define VCC_SENS23_R2 287000
#define VP5_R1 523000
#define VP5_R2 100000
#define VLDO18_R1 357000
#define VLDO18_R2 287000
#define PINSTRAPPED_OVEN 1
#define REF_FIXED_TENTH_MV 8000
#define REF_VAR_0_TENTH_MV 3625
#define REF_VAR_STEP_TENTH_MV 125
#define DEAFULT_TIMEOUT 300 /* number of retries testing pgood before giving up */
struct pwr_gpio_t {
const char * label;
int pin;
int dir; /* direction: 0 - in, 1 - out*/
int out_val; /* output value */
};
struct elphel393_pwr_data_t {
int chip_i2c_addr[3];
struct device * ltc3489_dev;
struct pwr_gpio_t pwr_gpio [16];
int simulate; /* do not perform actual i2c writes */
struct mutex lock;
int pgoot_timeout;
int pinstrapped_oven;
};
struct voltage_reg_t {
const char * name;
int r1; /* resistor in ohms, if <=0 - r2 is voltage in mv */
int r2; /* resistor in ohms, if r1<=0 - voltage in mv */
int awe_ref; /* 0 - no control, -1 - margining VP10, -2 - margining VP18 */
int awe_en; /* 0 - no control, negative - -1-gpio_index */
int awe_pgood; /* 0 - no status , negative - -1-gpio_index */
int mask_pgood; /* 1 - temporarily disable pgood when turning on/changing voltage */
int awe_slew;
};
static struct voltage_reg_t voltage_reg[]={
{
.name="vp15",
.r1=VP15_R1,
.r2=VP15_R2,
.awe_ref=LTC3589_AWE_B1DTV1_REF,
.awe_en=0,
.awe_pgood=LTC3589_AWE_PGSTAT_SD1,
.mask_pgood=1,
.awe_slew=LTC3589_AWE_VCCR_SLEW_SD1
},
{
.name="vcc_sens01",
.r1=VCC_SENS01_R1,
.r2=VCC_SENS01_R2,
.awe_ref=LTC3589_AWE_B2DTV1_REF,
.awe_en=LTC3589_AWE_OVEN_EN_SD2,
.awe_pgood=LTC3589_AWE_PGSTAT_SD2,
.mask_pgood=1,
.awe_slew=LTC3589_AWE_VCCR_SLEW_SD2
},
{
.name="vcc_sens23",
.r1=VCC_SENS23_R1,
.r2=VCC_SENS23_R2,
.awe_ref=LTC3589_AWE_B3DTV1_REF,
.awe_en=LTC3589_AWE_OVEN_EN_SD3,
.awe_pgood=LTC3589_AWE_PGSTAT_SD3,
.mask_pgood=1,
.awe_slew=LTC3589_AWE_VCCR_SLEW_SD3
},
{
.name="vp5",
.r1=VP5_R1,
.r2=VP5_R2,
.awe_ref=0,
.awe_en=LTC3589_AWE_OVEN_EN_BB,
.awe_pgood=LTC3589_AWE_PGSTAT_BB,
.mask_pgood=1,
.awe_slew=0
},
{
.name="vldo18",
.r1=VLDO18_R1,
.r2=VLDO18_R2,
.awe_ref=0,
.awe_en= 0,
.awe_pgood=LTC3589_AWE_PGSTAT_LDO1,
.mask_pgood=1,
.awe_slew=0
},
{
.name="vp33sens01",
.r1=-1,
.r2=33000,
.awe_ref=0,
.awe_en= -7, /* SENSPWREN0 */
.awe_pgood=0,
.mask_pgood=1,
.awe_slew=0
},
{
.name="vp33sens23",
.r1=-1,
.r2=33000,
.awe_ref=0,
.awe_en= -8, /* SENSPWREN1 */
.awe_pgood=0,
.mask_pgood=1,
.awe_slew=0
},
{
.name="mmtavcc10",
.r1=-1,
.r2=10000,
.awe_ref=0,
.awe_en= 0,
.awe_pgood=-15, /* MGTAVTTGOOD */
.mask_pgood=1,
.awe_slew=0
},
{
.name="mmtavtt12",
.r1=-1,
.r2=12000,
.awe_ref=0,
.awe_en= 0,
.awe_pgood=-15, /* MGTAVTTGOOD */
.mask_pgood=1,
.awe_slew=0
},
{
.name="vp10",
.r1=-1,
.r2=10000,
.awe_ref=-1,
.awe_en= 0,
.awe_pgood=-16, /* PGOOD18 */
.mask_pgood=1,
.awe_slew=0
},
{
.name="vp18",
.r1=-1,
.r2=18000,
.awe_ref=-2,
.awe_en= 0,
.awe_pgood=-16, /* PGOOD18 */
.mask_pgood=1,
.awe_slew=0
},
};
static struct pwr_gpio_t pwr_gpio[16]={
/* 0x20: */
{"PWR_MGB1", 0, 0, 0}, /* 1.8V margining magnitude (0 - 5%, 1 - 10%, float - 15%) */
{"PWR_MG1", 1, 0, 0}, /* 1.8V margining enable 0 - negative margining, 1 - positive margining, float - no margining */
{"PWR_MGB0", 2, 0, 0}, /* 1.0V margining magnitude (0 - 5%, 1 - 10%, float - 15%) */
{"PWR_MG0", 3, 0, 0}, /* 1.0V margining enable 0 - negative margining, 1 - positive margining, float - no margining */
{"PWR_FQ0", 4, 0, 0}, /* float - nominal frequency (should float for SS), 0 - 0.67 nominal frequency, 1 - 1.5 nominal frequency */
{"PWR_SS", 5, 0, 0}, /* Spread spectrum, 0 or float - spread spectrum disabled */
{"SENSPWREN0", 6, 0, 0}, /* 1 - enable 3.3 power to sensor connectors J6 and J7 (0 or float - disable) */
{"SENSPWREN1", 7, 0, 0}, /* 1 - enable 3.3 power to sensor connectors J8 and J9 (0 or float - disable) */
/* 0x21: */
{"NSHUTDOWN", 8, 0, 0}, /* (pulled up). 0 - shutdown, 1 normal */
{"DIS_POR", 9, 0, 0}, /* (pulled down). 0 - normal, 1 - disable POR generation on PGOOD deassertion (needed whil changing voltages) */
{ NULL, 10, 0, 0}, /* Not connected */
{ NULL, 11, 0, 0}, /* Not connected */
{ NULL, 12, 0, 0}, /* Not connected */
{ NULL, 13, 0, 0}, /* Not connected */
{"MGTAVTTGOOD",14, 0, 0}, /* (input) 1.2V linear regulator status (generated from 1.8V) */
{"PGOOD18", 15, 0, 0} /* (input). Combines other voltages, can be monitored when DIS_POR is activated */
};
static int make_group (struct device *dev, const char * name,
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf),
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count));
static ssize_t simulate_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t simulate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t outputs_all_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t configs_all_show(struct device *dev, struct device_attribute *attr, char *buf);
#if 0
static ssize_t output_state_show(struct device *dev, struct device_attribute *attr, char *buf);
#endif
static ssize_t output_en_output_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_en_output_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t outputs_pgood_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t channels_en_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t channels_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t channels_dis_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t channels_dis_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t output_ref_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_ref_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t pgood_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t pbad_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t enable_por_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t enable_por_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static int por_ctrl(struct device *dev, int disable_por);
static int get_and_disable_por(struct device *dev, int chn_bits, int * old_dis_por);
static int reenable_por(struct device *dev);
static int wait_all_pgood(struct device *dev);
static int list_chn_bits(char * buf, int chn_bits);
static int parse_chn_bits(const char * buf);
static int get_enabled_mask(struct device *dev);
static int set_enabled_by_mask(struct device *dev, int chn_bits, int enable);
static int slew_by_mask(struct device *dev, int chn_bits);
static int get_voltage_channel(const char * name);
static int get_gpio_index_by_name(const char * name);
static int gpio_conf_by_index(struct device *dev,int gpio_index, int dir, int val);
static int get_gpio_pwr_mgx_indices(int chn, int * indices); /* chn = 0 (VP10) or 1 (VP18) */
static int get_volt_mv(struct device *dev, int chn);
static int set_volt_mv(struct device *dev, int chn, int v_mv);
static int get_enable(struct device *dev, int chn);
static int set_enable(struct device *dev, int chn, int enable);
static int get_pgood(struct device *dev, int chn);
/*
Voltages:
VP10 (on at power up, nominal 1.0V)
VP18 (on at power up, nomianl 1.8V)
VP15 (SW1, on by pinstrap, nominal 1.5V - may be reduced to 1.35 later)
VCC_SENS01 (SW2, nominal 1.8V, max 2.8V)
VCC_SENS23 (SW3, nominal 1.8V, max 2.8V)
VP5 (nominal 5.0V, not software programmed)
VLDO18 (LDO1 - always on)
VP33SENS0 - 3.3V to sensors J6,J7
VP33SESN1 - 3.3V to sensors J8,J9
MGTAVCC10 - 1.0 V, linear from VP18 (pgood controls MGTAVTT12)
MGTAVTT12 - 1.2 V, linear from VP18 (pgood available, means both)
LTC3589 used channels : LDO1, SW1, SW2, SW3, BB
TODO: Change VCC_SENS01_R1, VCC_SENS23_R1 to 787K (now 487)
*/
/* root directory */
static DEVICE_ATTR(simulate, SYSFS_PERMISSIONS, simulate_show, simulate_store);
static DEVICE_ATTR(output_state, SYSFS_PERMISSIONS & SYSFS_READONLY, outputs_all_show, NULL);
static DEVICE_ATTR(configs, SYSFS_PERMISSIONS & SYSFS_READONLY, configs_all_show, NULL);
static DEVICE_ATTR(channels_en, SYSFS_PERMISSIONS, channels_en_show, channels_en_store);
static DEVICE_ATTR(channels_dis,SYSFS_PERMISSIONS, channels_dis_show, channels_dis_store);
static DEVICE_ATTR(power_good, SYSFS_PERMISSIONS & SYSFS_READONLY, pgood_show, NULL);
static DEVICE_ATTR(power_bad, SYSFS_PERMISSIONS & SYSFS_READONLY, pbad_show, NULL);
static DEVICE_ATTR(enable_por, SYSFS_PERMISSIONS, enable_por_show, enable_por_store);
static struct attribute *root_dev_attrs[] = {
&dev_attr_simulate.attr,
&dev_attr_output_state.attr,
&dev_attr_configs.attr,
&dev_attr_channels_en.attr,
&dev_attr_channels_dis.attr,
&dev_attr_power_good.attr,
&dev_attr_power_bad.attr,
&dev_attr_enable_por.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static int make_group (struct device *dev, const char * name,
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf),
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count))
{
int retval=-1;
int index;
struct attribute **pattrs; /* array of pointers to attibutes */
struct device_attribute *dev_attrs;
struct attribute_group *attr_group;
pattrs = devm_kzalloc(dev,(ARRAY_SIZE(voltage_reg)+1)*sizeof(pattrs[0]), GFP_KERNEL);
if (!pattrs) return -ENOMEM;
dev_attrs = devm_kzalloc(dev, ARRAY_SIZE(voltage_reg)*sizeof(dev_attrs[0]), GFP_KERNEL);
if (!dev_attrs) return -ENOMEM;
attr_group = devm_kzalloc(dev, sizeof(*attr_group), GFP_KERNEL);
if (!attr_group) return -ENOMEM;
memset(dev_attrs, 0, ARRAY_SIZE(voltage_reg)*sizeof(dev_attrs[0]));
memset(attr_group, 0, sizeof(*attr_group));
for (index=0;indexname = name;
attr_group->attrs =pattrs;
dev_dbg(dev,"name=%s, &dev->kobj=0x%08x\n",attr_group->name, (int) (&dev->kobj));
if (&dev->kobj) {
retval = sysfs_create_group(&dev->kobj, attr_group);
}
return retval;
}
static ssize_t simulate_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
return sprintf(buf, "%d\n",clientdata->simulate);
}
static ssize_t simulate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev);
sscanf(buf, "%du", &clientdata->simulate);
ltc3589_set_simulate(ltc3589_client, clientdata->simulate);
return count;
}
static ssize_t outputs_all_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn, pg;
char * cp = buf;
for (chn=0;chn0)?get_pgood(dev, chn):-1;
buf+=sprintf(buf,"%s: %s %d mV%s\n",
voltage_reg[chn].name,
get_enable(dev, chn)?"ON":"OFF",
get_volt_mv(dev, chn),
(pg==1)?", power good":((pg==0)?", power is NOT good":"")
);
}
return buf-cp;
}
static ssize_t configs_all_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn, pg;
char * cp = buf;
for (chn=0;chnattr.name);
if (chn<0) return chn;
pg=get_pgood(dev, chn);
return sprintf(buf,"%s: %s %d mV, %s\n",
voltage_reg[chn].name,
get_enable(dev, chn)?"ON":"OFF",
get_volt_mv(dev, chn),
(pg=1)?"power good":((pg==0)?"power is NOT good":"")
);
}
#endif
static ssize_t output_en_output_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn;
chn=get_voltage_channel(attr->attr.name);
if (chn<0) return chn;
return sprintf(buf,"%d\n", get_enable(dev, chn));
}
static ssize_t output_en_output_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, enable;
chn=get_voltage_channel(attr->attr.name);
if (chn<0) return chn;
sscanf(buf, "%du", &enable);
return count;
}
static ssize_t outputs_pgood_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn;
chn=get_voltage_channel(attr->attr.name);
if (chn<0) return chn;
return sprintf(buf,"%d\n", get_pgood(dev, chn));
}
static ssize_t channels_en_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn_bits;
char * cp=buf;
chn_bits=get_enabled_mask(dev);
if (chn_bits<0) return chn_bits;
buf+=list_chn_bits(buf, chn_bits);
buf+=sprintf(buf,"\n");
return buf-cp;
}
/* also slews DAC(s) if applilcable. Call after changing voltage on enabled channels */
static ssize_t channels_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn_bits,rc,old_dis_por,pre_disabled;
chn_bits=parse_chn_bits(buf);
pre_disabled=get_and_disable_por(dev, chn_bits, &old_dis_por);
if (pre_disabled<0) return pre_disabled;
rc=slew_by_mask(dev, chn_bits); /* slew if needed - before enabling, waits for slew over */
if (rc<0) return rc;
rc=set_enabled_by_mask(dev, chn_bits, 1);
if (rc<0) return rc;
if (pre_disabled && (old_dis_por==0)){
rc=reenable_por(dev); /* will wait pgood */
if (rc<0) return rc;
}
return count;
}
static ssize_t channels_dis_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn_bits;
char * cp=buf;
chn_bits=get_enabled_mask(dev);
if (chn_bits<0) return chn_bits;
chn_bits=~chn_bits;
buf+=list_chn_bits(buf, chn_bits);
buf+=sprintf(buf,"\n");
return buf-cp;
}
static ssize_t channels_dis_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn_bits,rc;
chn_bits=parse_chn_bits(buf);
rc=set_enabled_by_mask(dev, chn_bits, 0);
if (rc<0) return rc;
return count;
}
static ssize_t output_ref_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn;
chn=get_voltage_channel(attr->attr.name);
if (chn<0) return chn;
return sprintf(buf,"%d\n",get_volt_mv(dev, chn));
}
static ssize_t output_ref_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn, v_mv;
int rc,old_dis_por,pre_disabled;
chn=get_voltage_channel(attr->attr.name);
if (chn<0) return chn;
/* if output was enabled, and pgood negation may cause POR, disable POR (later restore) */
if (get_enable(dev,chn)) pre_disabled=get_and_disable_por(dev, 1<0) pgood_bits |= (1<pwr_gpio[gpio_disable_por_index].out_val)?0:1);
}
/* When enable_por is set to 1, it first waits for PGOOD and does not enable POR on error */
static ssize_t enable_por_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int en_por,rc;
sscanf(buf, "%du", &en_por);
if (en_por) rc=reenable_por(dev); /* will wait pgood, then enable POR */
else rc=por_ctrl(dev, 1); /* disable POR */
if (rc<0) return rc;
return count;
}
int por_ctrl(struct device *dev, int disable_por)
{
int gpio_disable_por_index=get_gpio_index_by_name("DIS_POR");
if (gpio_disable_por_index<0) return gpio_disable_por_index;
return gpio_conf_by_index(dev, gpio_disable_por_index, 1, disable_por);
}
/*
* disable POR (if needed) before changing value or enabling one of the voltages
* chn_bits - 1 bit per channel
*/
static int get_and_disable_por(struct device *dev, int chn_bits, int * old_dis_por)
{
int rc,chn;
int gpio_disable_por_index;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
gpio_disable_por_index=get_gpio_index_by_name("DIS_POR");
if (gpio_disable_por_index<0) return gpio_disable_por_index;
old_dis_por[0]=clientdata->pwr_gpio[gpio_disable_por_index].out_val;
for (chn=0;chn=ARRAY_SIZE(voltage_reg)) return 0; /* POR was not required to be disabled */
rc = gpio_conf_by_index(dev, gpio_disable_por_index, 1, 1); /* out turn on "disable_por" */
if (rc<0) return rc;
return 1; /* pgood-based POR was disabled (could already be disabled)*/
}
/* call if POR was diasabled before changing voltage (value or enabling), after waiting for pgood*/
static int reenable_por(struct device *dev)
{
int gpio_disable_por_index, rc;
gpio_disable_por_index=get_gpio_index_by_name("DIS_POR");
if (gpio_disable_por_index<0) return gpio_disable_por_index;
if (((rc=wait_all_pgood(dev)))<0) return rc;
return gpio_conf_by_index(dev, gpio_disable_por_index, 1, 0); /* out turn off "disable_por" */
}
static int wait_all_pgood(struct device *dev)
{
int ntry,chn,all_good=0;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
for (ntry=0;ntrypgoot_timeout;ntry++){
all_good=1;
for (chn=0;chn0) && (get_pgood(dev,chn)!=1)){ /* enabled or always enabled */
all_good=0;
break;
}
}
if (all_good) break; /* all enabled channels that have pgood control are good */
}
if (!all_good) return -EAGAIN;
return 0;
}
static int list_chn_bits(char * buf, int chn_bits)
{
int chn;
char * cp=buf;
for (chn=0;chn0) en_mask|= (1<0){
awe |= voltage_reg[chn].awe_en;
}
}
awe &= 0xff; /* just WE mask */
if (awe){
dev_dbg(dev,"set_enabled_by_mask(), cumulative awe=0x%x\n",awe);
ltc3589_client = to_i2c_client(clientdata->ltc3489_dev);
oven=ltc3589_read_field (ltc3589_client, LTC3589_AWE_OVEN);
if (oven<0) return oven;
if (enable) oven |= awe;
else oven &= ~awe;
return ltc3589_write_field (ltc3589_client, oven, LTC3589_AWE_OVEN);
}
return 0;
}
static int slew_by_mask(struct device *dev, int chn_bits)
{
/* assuming all slew bits in LTC3589 to be in a single register (LTC3589_AWE_OVEN) */
int chn, slew=0,rc,ntry;
u32 adwe;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
struct i2c_client *ltc3589_client;
ltc3589_client = to_i2c_client(clientdata->ltc3489_dev);
dev_dbg(dev,"slew_by_mask(dev,0x%x)\n",chn_bits);
for (chn=0;chn%d (slew = 0x%x)\n",adwe,rc,slew);
if (rc<0) return rc;
/* wait slew over */
for (ntry=0;ntrypgoot_timeout;ntry++){
rc=ltc3589_read_field(ltc3589_client, LTC3589_AWE_VCCR);
dev_dbg(dev,"slew_by_mask():ltc3589_read_field(ltc3589_client, 0x%x)->0x%x(%d)\n",LTC3589_AWE_VCCR,rc,rc);
if (rc<0) return rc;
if ((rc & slew) ==0 ) break;
}
if (ntry>=clientdata->pgoot_timeout) return -EAGAIN;
}
return 0;
}
/* name should either completely match, or have "_*" suffix */
static int get_voltage_channel(const char * name)
{
int i;
for (i=0;i=ARRAY_SIZE(clientdata->pwr_gpio))) return -EINVAL;
if ((clientdata->pwr_gpio[gpio_index].dir==dir) && ((clientdata->pwr_gpio[gpio_index].out_val==val) || (dir==0))){
dev_dbg(dev,"GPIO#%d(index=%d) did not change: old dir=%d, new dir=%d, old val = %d, new val=%d\n",
clientdata->pwr_gpio[gpio_index].pin,
gpio_index,
clientdata->pwr_gpio[gpio_index].dir,
dir,
clientdata->pwr_gpio[gpio_index].out_val,
val);
return 0;
}
clientdata->pwr_gpio[gpio_index].dir=dir?1:0;
clientdata->pwr_gpio[gpio_index].out_val=val?1:0;
if (clientdata->pwr_gpio[gpio_index].dir){
if (!clientdata->simulate) rc=gpio_direction_output(clientdata->pwr_gpio[gpio_index].pin, clientdata->pwr_gpio[gpio_index].out_val);
dev_dbg(dev,"gpio_direction_output(%d,%d)->%d\n",clientdata->pwr_gpio[gpio_index].pin, clientdata->pwr_gpio[gpio_index].out_val,rc);
} else {
if (!clientdata->simulate) rc=gpio_direction_input(clientdata->pwr_gpio[gpio_index].pin);
dev_dbg(dev,"gpio_direction_input(%d)->%d\n",clientdata->pwr_gpio[gpio_index].pin,rc);
}
return rc;
}
static int get_gpio_pwr_mgx_indices(int chn, int * indices) /* chn = 0 (VP10) or 1 (VP18) */
{
indices[0]=get_gpio_index_by_name(chn?"PWR_MG1": "PWR_MG0");
indices[1]=get_gpio_index_by_name(chn?"PWR_MGB1":"PWR_MGB0");
return ((indices[0]>=0) && (indices[1]>=0))?0:-EINVAL;
}
/* calculate output voltage in mV */
static int get_volt_mv(struct device *dev, int chn)
{
int v_mv,ref,rc;
int pwr_mg_indices[2];
s64 num;
struct i2c_client *ltc3589_client;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL;
if (voltage_reg[chn].r1<=0) {
if (voltage_reg[chn].awe_ref<0) { /* vp10, vp18*/
rc= get_gpio_pwr_mgx_indices(-1-voltage_reg[chn].awe_ref,pwr_mg_indices); /* chn = 0 (VP10) or 1 (VP18) */
if (rc<0) return rc;
if (clientdata->pwr_gpio[pwr_mg_indices[0]].dir==0) ref=0;
else if (clientdata->pwr_gpio[pwr_mg_indices[0]].out_val) ref=1;
else ref=-1;
if (ref) {
if (clientdata->pwr_gpio[pwr_mg_indices[1]].dir==0) ref*=15;
else if (clientdata->pwr_gpio[pwr_mg_indices[1]].out_val) ref*=10;
else ref*= 5;
}
v_mv=(voltage_reg[chn].r2*(100+ref)*2+10)/2000;
} else { /* vp33sens01, vp33sens23, mmtavcc10, mmtavtt12 */
v_mv=(voltage_reg[chn].r2+5)/10;
}
} else if (voltage_reg[chn].awe_ref==0){ /* VP5, vldo18 */
#if 0
v_mv=(REF_FIXED_TENTH_MV*(voltage_reg[chn].r1+voltage_reg[chn].r2)+ 5*voltage_reg[chn].r2)/(10*voltage_reg[chn].r2);
#endif
num=((u64) REF_FIXED_TENTH_MV)* (voltage_reg[chn].r1+voltage_reg[chn].r2)+ 5*voltage_reg[chn].r2;
v_mv=(int) div64_u64(num, 10*voltage_reg[chn].r2);
dev_dbg(dev,"chn=%d REF_FIXED_TENTH_MV=%d .r1=%d .r2=%d v_mv=%d\n",chn, REF_FIXED_TENTH_MV,voltage_reg[chn].r1,voltage_reg[chn].r2,v_mv);
} else { /* vp15, vcc_sens01,vcc_sens23 */
ltc3589_client = to_i2c_client(clientdata->ltc3489_dev);
ref=ltc3589_read_field(ltc3589_client, voltage_reg[chn].awe_ref);
if (ref<0) return ref;
num=(REF_VAR_0_TENTH_MV+ REF_VAR_STEP_TENTH_MV* ref);
num=num*(voltage_reg[chn].r1+voltage_reg[chn].r2)+ 5*voltage_reg[chn].r2;
v_mv=div64_u64(num, 10*voltage_reg[chn].r2);
dev_dbg(dev,"chn=%d ref=%d .r1=%d .r2=%d v_mv=%d\n",chn, ref,voltage_reg[chn].r1,voltage_reg[chn].r2,v_mv);
}
return v_mv;
}
/* 0 - OK, <0 - error */
/* does not iclude disabling/re-enabling PoR */
static int set_volt_mv(struct device *dev, int chn, int v_mv)
{
int rc,index,d;
s64 num;
int pwr_mg_indices[2];
struct i2c_client *ltc3589_client;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL;
dev_dbg(dev,"set_volt_mv(dev,%d,%d),.r1=%d\n",chn,v_mv,voltage_reg[chn].r1);
if (voltage_reg[chn].r1<=0) {
if (voltage_reg[chn].awe_ref<0) { /* vp10, vp18*/
index=(400*v_mv+voltage_reg[chn].r2)/(2*voltage_reg[chn].r2);
dev_dbg(dev,"chn=%d v_mv=%d index=%d .r1=%d .r2=%d\n",chn, v_mv, index,voltage_reg[chn].r1,voltage_reg[chn].r2);
if ((index<17) || (index>23)) {
dev_err(dev,"specified voltage for %s is not in the range %dmV to %d mV\n", voltage_reg[chn].name,
(17*voltage_reg[chn].r2)/200,(23*voltage_reg[chn].r2)/200);
return -EINVAL;
}
/* disable -> chnage -> enable (if needed) */
rc= get_gpio_pwr_mgx_indices(-1-voltage_reg[chn].awe_ref,pwr_mg_indices); /* chn = 0 (VP10) or 1 (VP18) */
if (rc<0) return rc;
rc = gpio_conf_by_index(dev,pwr_mg_indices[0], 0, 0); /* disable margining */
if (rc < 0)return rc;
if (index !=20){
/* set margining absolute value */
switch (index) {
case 17:
case 23:
rc = gpio_conf_by_index(dev,pwr_mg_indices[1], 0, 0); /* float: +/- 15% */
break;
case 18:
case 22:
rc = gpio_conf_by_index(dev,pwr_mg_indices[1], 1, 1); /* out 1: +/- 10% */
break;
case 19:
case 21:
rc = gpio_conf_by_index(dev,pwr_mg_indices[1], 1, 0); /* out 0: +/- 5% */
break;
}
if (rc < 0)return rc;
/* set margining sign */
if (index >20) rc = gpio_conf_by_index(dev,pwr_mg_indices[0], 1, 1); /* out 1: positive margining */
else rc = gpio_conf_by_index(dev,pwr_mg_indices[0], 1, 0); /* out 0: negative margining */
if (rc < 0)return rc;
}
} else { /* vp33sens01, vp33sens23, mmtavcc10, mmtavtt12 */
return -EINVAL; /* voltage not regulated */
}
} else if (voltage_reg[chn].awe_ref==0){ /* VP5, vldo18 */
return -EINVAL; /* voltage not regulated */
} else { /* vp15, vcc_sens01,vcc_sens23 */
ltc3589_client = to_i2c_client(clientdata->ltc3489_dev);
#if 0
index=((10*v_mv*voltage_reg[chn].r2) -(REF_VAR_0_TENTH_MV-REF_VAR_STEP_TENTH_MV/2)*(voltage_reg[chn].r1+voltage_reg[chn].r2))/
((voltage_reg[chn].r1+voltage_reg[chn].r2)*REF_VAR_STEP_TENTH_MV);
num=(10*v_mv*voltage_reg[chn].r2) -(REF_VAR_0_TENTH_MV-REF_VAR_STEP_TENTH_MV/2);
num*=(voltage_reg[chn].r1+voltage_reg[chn].r2);
index=div64_u64(num, (voltage_reg[chn].r1+voltage_reg[chn].r2)*REF_VAR_STEP_TENTH_MV);
#endif
num= (10LL*v_mv*voltage_reg[chn].r2) - ((s64) (voltage_reg[chn].r1+voltage_reg[chn].r2))*REF_VAR_0_TENTH_MV;
d= REF_VAR_STEP_TENTH_MV*(voltage_reg[chn].r1+voltage_reg[chn].r2);
index=div64_u64(num +(d>>1), d);
dev_dbg(dev,"chn=%d v_mv=%d index=%d .r1=%d .r2=%d\n",chn, v_mv, index,voltage_reg[chn].r1,voltage_reg[chn].r2);
dev_dbg(dev,"index=%d\n",index);
if ((index<0) || (index>31)){
dev_err(dev,"chn=%d v_mv=%d index=%d .r1=%d .r2=%d\n",chn, v_mv, index,voltage_reg[chn].r1,voltage_reg[chn].r2);
dev_err(dev,"REF_VAR_0_TENTH_MV=%d REF_VAR_STEP_TENTH_MV=%d\n",REF_VAR_0_TENTH_MV,REF_VAR_STEP_TENTH_MV);
dev_err(dev,"specified voltage for %s is not in the range %dmV to %d mV\n", voltage_reg[chn].name,
(int) div64_u64((((u64)(REF_VAR_0_TENTH_MV+REF_VAR_STEP_TENTH_MV* 0))*(voltage_reg[chn].r1+voltage_reg[chn].r2)+5*voltage_reg[chn].r2),
10*voltage_reg[chn].r2),
(int) div64_u64((((u64)(REF_VAR_0_TENTH_MV+REF_VAR_STEP_TENTH_MV*31))*(voltage_reg[chn].r1+voltage_reg[chn].r2)+5*voltage_reg[chn].r2),
10*voltage_reg[chn].r2));
return -EINVAL;
}
dev_dbg(dev,"ltc3589_client->name= %s\n", ltc3589_client->name);
rc=ltc3589_write_field(ltc3589_client, index,voltage_reg[chn].awe_ref);
if (rc<0) return rc;
}
return 0;
}
/* get output enable state */
static int get_enable(struct device *dev, int chn)
{
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev);
if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL;
if (voltage_reg[chn].awe_en==0) {
return 2; /* always on */
} else if (voltage_reg[chn].awe_en>0){
if (clientdata->pinstrapped_oven & voltage_reg[chn].awe_en) return 1; /* pin-strapped on bit */
return ltc3589_read_field(ltc3589_client, voltage_reg[chn].awe_en);
} else {
return (clientdata->pwr_gpio[-1-voltage_reg[chn].awe_en].dir && clientdata->pwr_gpio[-1-voltage_reg[chn].awe_en].out_val)?1:0;
}
}
/* set output enable state */
static int set_enable(struct device *dev, int chn, int enable)
{
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev);
if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL;
if (voltage_reg[chn].awe_en==0) {
return -EINVAL; /* always on, not controlled */
} else if (voltage_reg[chn].awe_en>0){
return ltc3589_write_field(ltc3589_client, enable, voltage_reg[chn].awe_en);
} else {
return gpio_conf_by_index(dev,-1-voltage_reg[chn].awe_en, 1, enable);
}
}
/* get power good state */
static int get_pgood(struct device *dev, int chn)
{
int rc;
struct elphel393_pwr_data_t *clientdata=platform_get_drvdata(to_platform_device(dev));
struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev);
if ((chn<0) || (chn>=ARRAY_SIZE(voltage_reg))) return -EINVAL;
if (voltage_reg[chn].awe_pgood==0) {
if (((rc=get_enable(dev,chn)))<0) return rc; /* 0 - disabled */
return 2; /* no status available */
} else if (voltage_reg[chn].awe_pgood>0){
return ltc3589_read_field(ltc3589_client, voltage_reg[chn].awe_pgood);
} else {
/* return gpio_get_value(clientdata->pwr_gpio[-1-voltage_reg[chn].awe_pgood].pin); */
return gpio_get_value_cansleep(clientdata->pwr_gpio[-1-voltage_reg[chn].awe_pgood].pin);
}
}
static int elphel393_pwr_sysfs_register(struct platform_device *pdev)
{
int retval=0;
struct device *dev = &pdev->dev;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
if (((retval = make_group (dev, "voltages_mv", output_ref_show, output_ref_store)))<0) return retval;
if (((retval = make_group (dev, "outputs_en", output_en_output_show, output_en_output_store)))<0) return retval;
if (((retval = make_group (dev, "outputs_pgood", outputs_pgood_show, NULL)))<0) return retval;
}
return retval;
}
static void elphel393_pwr_init_of_i2caddr(struct platform_device *pdev)
{
const __be32 * config_data;
int len,i;
struct device_node *node = pdev->dev.of_node;
struct elphel393_pwr_data_t *clientdata = platform_get_drvdata(pdev);
if (node) {
config_data = of_get_property(node, "elphel393_pwr,i2c_chips", &len);
if (config_data){
len /= sizeof(*config_data);
dev_dbg(&pdev->dev,"Found %d items in 'elphel393_pwr,i2c_chips' in the Device Tree\n",len);
if (len!= ARRAY_SIZE(clientdata->chip_i2c_addr)){
dev_err(&pdev->dev,"Got %d items in 'elphel393_pwr,i2c_chips', expected %d\n",len,ARRAY_SIZE(clientdata->chip_i2c_addr));
return;
}
for (i=0;ichip_i2c_addr[i]=be32_to_cpup(&config_data[i]);
}
}
}
static void elphel393_pwr_init_of(struct platform_device *pdev)
{
const __be32 * config_data;
const char * config_string;
char str[40];
int len,chn,pre_disabled,old_dis_por,rc,chn_bits;
struct device_node *node = pdev->dev.of_node;
struct elphel393_pwr_data_t *clientdata = platform_get_drvdata(pdev);
struct i2c_client *ltc3589_client= to_i2c_client(clientdata->ltc3489_dev);
if (node) {
/* find resistor values */
for (chn=0;chn0)){
dev_dbg(&pdev->dev,"Found %s=<%d>\n",str,be32_to_cpup(&config_data[0]));
voltage_reg[chn].r1=be32_to_cpup(&config_data[0]);
}
sprintf(str,"elphel393_pwr,%s.r2",voltage_reg[chn].name);
config_data = of_get_property(node, str, &len);
if (config_data && (len>0)){
dev_dbg(&pdev->dev,"Found %s=<%d>\n",str,be32_to_cpup(&config_data[0]));
voltage_reg[chn].r2=be32_to_cpup(&config_data[0]);
}
}
/* which channels are enabled by pin-strapping */
config_data = of_get_property(node, "elphel393_pwr,pinstrapped_oven", &len);
if (config_data && (len>0)){
dev_dbg(&pdev->dev,"Found elphel393_pwr,pinstrapped_oven=<%d>\n",be32_to_cpup(&config_data[0]));
clientdata->pinstrapped_oven=be32_to_cpup(&config_data[0]);
}
/* debug mode - simulate only, no actual power supply control */
config_data = of_get_property(node, "elphel393_pwr,simulate", &len);
if (config_data && (len>0)){
dev_dbg(&pdev->dev,"Found elphel393_pwr,simulate=<%d>\n",be32_to_cpup(&config_data[0]));
clientdata->simulate=config_data[0]?1:0;
ltc3589_set_simulate(ltc3589_client, clientdata->simulate);
}
/* disable output voltages (not likely to be needed - maybe for warm reboot) */
config_string = of_get_property(node, "elphel393_pwr,channels_disable", &len);
if (config_string){
dev_dbg(&pdev->dev,"Found elphel393_pwr,channels_disable=\"%s\"\n",config_string);
chn_bits=parse_chn_bits(config_string);
rc=set_enabled_by_mask(&pdev->dev, chn_bits, 0);
if (rc<0) return;
}
/* set output voltages (target voltages, in mV) */
for (chn=0;chn0)){
dev_dbg(&pdev->dev,"Found %s=<%d>\n",str,be32_to_cpup(&config_data[0]));
if (get_enable(&pdev->dev,chn)) pre_disabled=get_and_disable_por(&pdev->dev, 1<dev,"pre_disabled=%d\n",pre_disabled);
rc=set_volt_mv(&pdev->dev, chn,be32_to_cpup(&config_data[0]));
dev_dbg(&pdev->dev,"set_volt_mv()->%d\n",rc);
if (rc<0) return;
if (pre_disabled && (old_dis_por==0)){
rc=reenable_por(&pdev->dev); /* will wait pgood */
if (rc<0){
dev_err(&pdev->dev,"Timeout during wait for power good after chnging voltage for %s before re-enabling POR on power loss\n",\
voltage_reg[chn].name);
return;
}
}
}
}
/* enable output voltages */
config_string = of_get_property(node, "elphel393_pwr,channels_enable", &len);
if (config_string){
dev_dbg(&pdev->dev,"Found elphel393_pwr,channels_enable=\"%s\"\n",config_string);
chn_bits=parse_chn_bits(config_string);
pre_disabled=get_and_disable_por(&pdev->dev, chn_bits, &old_dis_por);
if (pre_disabled<0) return;
rc=slew_by_mask(&pdev->dev, chn_bits); /* slew if needed - before enabling, waits for slew over */
if (rc<0) {
dev_err(&pdev->dev,"Timeout during wait for slew over\n");
return;
}
rc=set_enabled_by_mask(&pdev->dev, chn_bits, 1);
if (rc<0) return;
if (pre_disabled && (old_dis_por==0)){
rc=reenable_por(&pdev->dev); /* will wait pgood */
if (rc<0) {
dev_err(&pdev->dev,"Timeout during wait for power good before re-enabling POR on power loss\n");
return;
}
}
}
}
dev_info(&pdev->dev,"elphel393_pwr configuration done\n");
}
static int device_by_i2c_addr_match(struct device *dev, void *data)
{
struct i2c_client *client = to_i2c_client(dev);
int *addr = (int *)data;
dev_dbg(dev,"addr_given=0x%02x, addr found=0x%02x\n",addr[0],(int) client->addr);
return i2c_verify_client(dev) && (client->addr==addr[0]);
}
static struct device * find_device_by_i2c_addr(int address)
{
return bus_find_device(&i2c_bus_type, NULL, &address, device_by_i2c_addr_match);
}
static int i2c_addr_gpiochip_match(struct gpio_chip *chip, void *data)
{
struct i2c_client *client = to_i2c_client(chip->dev);
int *addr = (int *)data;
dev_dbg(chip->dev,"addr_given=0x%02x, addr found=0x%02x\n",addr[0],(int) client->addr);
return i2c_verify_client(chip->dev) && (client->addr==addr[0]);
}
static int elphel393_pwr_probe(struct platform_device *pdev)
{
struct gpio_chip *chip;
// struct device * ltc3489_dev;
int i,rc;
int base[2];
struct i2c_client *ltc3589_client;
struct elphel393_pwr_data_t *clientdata = NULL;
dev_info(&pdev->dev,"Probing elphel393-pwr\n");
clientdata = devm_kzalloc(&pdev->dev, sizeof(*clientdata), GFP_KERNEL);
clientdata->pgoot_timeout=DEAFULT_TIMEOUT;
clientdata->pinstrapped_oven=PINSTRAPPED_OVEN;
clientdata->chip_i2c_addr[0]=0x20;
clientdata->chip_i2c_addr[1]=0x21;
clientdata->chip_i2c_addr[2]=0x34;
platform_set_drvdata(pdev, clientdata);
elphel393_pwr_sysfs_register(pdev);
// elphel393_pwr_init_of(pdev);
elphel393_pwr_init_of_i2caddr(pdev);
mutex_init(&clientdata->lock);
/* locate GPIO chips by i2c address */
for (i=0;i<2;i++){
chip = gpiochip_find(&clientdata->chip_i2c_addr[i], i2c_addr_gpiochip_match);
base[i]=chip->base;
dev_dbg(&pdev->dev,"Found gpio_chip with i2c_addr=0x%02x, label=%s, base=0x%x\n",clientdata->chip_i2c_addr[i],chip->label,base[i]);
}
for (i=0;ipwr_gpio[i].label=pwr_gpio[i].label;
clientdata->pwr_gpio[i].pin=base[i>>3]+(i & 7);
clientdata->pwr_gpio[i].dir=0; /* input */
clientdata->pwr_gpio[i].out_val=0;
rc=gpio_request(clientdata->pwr_gpio[i].pin, clientdata->pwr_gpio[i].label);
if (rc<0){
dev_err(&pdev->dev," Failed to get GPIO[%d] with label %s\n",clientdata->pwr_gpio[i].pin,clientdata->pwr_gpio[i].label);
return rc;
} else {
dev_dbg(&pdev->dev,"Confirmed request GPIO[%d] with label %s\n",clientdata->pwr_gpio[i].pin,clientdata->pwr_gpio[i].label);
}
}
/* find ltc3589 */
clientdata->ltc3489_dev=find_device_by_i2c_addr(LTC3589_ADDR);
if (!clientdata->ltc3489_dev){
dev_err(&pdev->dev," Failed to find LTC3489 with i2c address 0x%02x\n",LTC3589_ADDR);
return -EIO;
}
ltc3589_client = to_i2c_client(clientdata->ltc3489_dev);
dev_dbg(&pdev->dev,"Located %s with i2c address 0x%02x\n",ltc3589_client->name,LTC3589_ADDR);
dev_dbg(&pdev->dev,"LTC3589 status= 0x%02x\n",ltc3589_read_field(ltc3589_client, LTC3589_AWE_PGSTAT));
elphel393_pwr_init_of(pdev);
return 0;
}
static int elphel393_pwr_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev,"Removing elphel393-pwr");
return 0;
}
static struct of_device_id elphel393_pwr_of_match[] = {
{ .compatible = "elphel,elphel393-pwr-1.00", },
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, elphel393_pwr_of_match);
static struct platform_driver elphel393_pwr = {
.probe = elphel393_pwr_probe,
.remove = elphel393_pwr_remove,
.driver = {
.name = "elphel393-pwr",
.owner = THIS_MODULE,
.of_match_table = elphel393_pwr_of_match,
.pm = NULL, /* power management */
},
};
module_platform_driver(elphel393_pwr);
MODULE_AUTHOR("Andrey Filippov ");
MODULE_DESCRIPTION("Elphel 10393 power supply control");
MODULE_LICENSE("GPL");
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/misc/ 0000775 0000000 0000000 00000000000 12666203034 0024377 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/misc/ltc3589.c 0000664 0000000 0000000 00000131465 12666203034 0025670 0 ustar 00root root 0000000 0000000 /*!***************************************************************************
*! FILE NAME : ltc3589.c
*! DESCRIPTION: control of the Linear Technology LTC3589 8-channel voltage regulator
*! Copyright (C) 2013 Elphel, 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 3 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 .
*/
#undef DEBUG /* should be before linux/module.h - enables dev_dbg at boot in this file */
#include
#include
#include
#include
#include
#include
#include
#include
#define DRV_VERSION "1.0"
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
#define CACHE_INIT 1
#define CACHE_VOLAT 2
#define LAST_REG 0x33
struct ltc3589_cache_t {
u8 flags;
u8 data;
};
struct ltc3589_data_t {
int reg_addr; /* used for raw register r/w */
int simulate; /* do not perform actual i2c writes */
struct mutex lock;
struct ltc3589_cache_t cache[LAST_REG+1];
};
static struct i2c_device_id ltc3589_id[] = {
{ "ltc3589", 0 },
{ }
};
struct named_fields_t {
char *name;
u32 awe;
};
static const struct named_fields_t status_fields[]={
{"power_good", LTC3589_AWE_PGSTAT},
{"pgood_ldo1", LTC3589_AWE_PGSTAT_LDO1},
{"pgood_sd1", LTC3589_AWE_PGSTAT_SD1},
{"pgood_sd2", LTC3589_AWE_PGSTAT_SD2},
{"pgood_sd3", LTC3589_AWE_PGSTAT_SD3},
{"pgood_bb", LTC3589_AWE_PGSTAT_BB},
{"pgood_ldo2", LTC3589_AWE_PGSTAT_LDO2},
{"pgood_ldo3", LTC3589_AWE_PGSTAT_LDO3},
{"pgood_ldo4", LTC3589_AWE_PGSTAT_LDO4},
{"irqstat", LTC3589_AWE_IRQSTAT},
{"irqstat_pgoot_timeout",LTC3589_AWE_IRQSTAT_PGOOD_TIMOUT},
{"irqstat_near_uv", LTC3589_AWE_IRQSTAT_NEAR_UV},
{"irqstat_hard_uv", LTC3589_AWE_IRQSTAT_HARD_UV},
{"irqstat_near_therm", LTC3589_AWE_IRQSTAT_NEAR_THERM},
{"irqstat_hard_therm", LTC3589_AWE_IRQSTAT_HARD_THERM},
};
static const struct named_fields_t named_fields[]={
{"ref1_sd1", LTC3589_AWE_B1DTV1_REF},
{"ref2_sd1", LTC3589_AWE_B1DTV2_REF},
{"ref1_sd2", LTC3589_AWE_B2DTV1_REF},
{"ref2_sd2", LTC3589_AWE_B2DTV2_REF},
{"ref1_sd3", LTC3589_AWE_B3DTV1_REF},
{"ref2_sd3", LTC3589_AWE_B3DTV2_REF},
{"ref1_ldo2", LTC3589_AWE_L2DTV1_REF},
{"ref2_ldo2", LTC3589_AWE_L2DTV2_REF},
{"ref_ldo4", LTC3589_AWE_B1DTV1_REF},
{"dv_dt_sd1", LTC3589_AWE_B1DTV1_DVDT},
{"pgood_mask_sd1", LTC3589_AWE_B1DTV1_PGMASK},
{"pgood_mask_sd2", LTC3589_AWE_B2DTV1_PGMASK},
{"pgood_mask_sd3", LTC3589_AWE_B3DTV1_PGMASK},
{"pgood_mask_ldo21",LTC3589_AWE_L2DTV1_PGMASK},
{"clock_rate_sd1", LTC3589_AWE_B1DTV2_CLKRATE},
{"clock_rate_sd2", LTC3589_AWE_B2DTV2_CLKRATE},
{"clock_rate_sd3", LTC3589_AWE_B3DTV2_CLKRATE},
{"clock_phase_sd1", LTC3589_AWE_B1DTV2_PHASE},
{"clock_phase_sd2", LTC3589_AWE_B2DTV2_PHASE},
{"clock_phase_sd3", LTC3589_AWE_B3DTV2_PHASE},
{"keep_alive_sd1", LTC3589_AWE_B1DTV2_KEEP_ALIVE},
{"keep_alive_sd2", LTC3589_AWE_B2DTV2_KEEP_ALIVE},
{"keep_alive_sd3", LTC3589_AWE_B3DTV2_KEEP_ALIVE},
{"keep_alive_ldo2", LTC3589_AWE_L2DTV1_KEEP_ALIVE},
{"slew_rate_sd1", LTC3589_AWE_VRRCR_SD1},
{"slew_rate_sd2", LTC3589_AWE_VRRCR_SD2},
{"slew_rate_sd3", LTC3589_AWE_VRRCR_SD3},
{"slew_rate_ldo2", LTC3589_AWE_VRRCR_LDO2},
{"oven_ldo4", LTC3589_AWE_L2DTV2_MODE_LDO4},
{"oven_only", LTC3589_AWE_OVEN_ONLY},
};
static const int volatile_registers[]={LTC3589_AWE_IRQSTAT_PGOOD_TIMOUT, LTC3589_AWE_PGSTAT_LDO1, LTC3589_AWE_VCCR,-1};
static const char * chn_names[]={"SD1","SD2","SD3","BB","LDO1","LDO2","LDO3","LDO4"};
static const char * modes[]={"continuous","burst","pulse_skip","invalid"};
static const char * pwr_states[]={"power_off","power_on"};
static const char * wait_states[]={"no_wait", "wait"};
static const char * reference_sel[]={"reference_sel1", "reference_sel2"};
/* (register_address << 8) | mask */
static const u32 register_masks[]= {
0x07ff,0x12ff,0x20ff,0x24ff,0x25ff,0x26ff,0x27ff,
0x29ff,0x2aff,0x32ff,0x33ff,0x10ff};
static int make_status_fields(struct device *dev);
static int make_bit_fields(struct device *dev);
static ssize_t invalidate_cache_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t simulate_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t simulate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t raw_address_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t raw_data_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t raw_hex_address_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_hex_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t raw_hex_data_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_hex_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t raw_hex_all_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_hex_adwe_help_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_hex_adwe_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t raw_hex_adwe_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t power_wait_on_off_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t power_wait_on_off_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t pgood_timeout_inhibit_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t pgood_timeout_inhibit_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t mode_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t reference_select_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t reference_select_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t reference_select_go_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t reference_select_go_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t field_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t field_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t get_field_value (struct device *dev, const char* name, char *buf, int newline);
static ssize_t irq_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t irq_show_txt (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t pwr_bad_good_show (struct device *dev, struct device_attribute *attr, char *buf);
static int get_chn_mode(struct device *dev, char *buf, int chn); /* 0..3 */
static int get_chn_pwr(struct device *dev, char *buf, int chn); /* 0..7 */
static int get_chn_wait(struct device *dev, char *buf, int chn); /* 0..7 */
static int get_ref_sel_go(struct device *dev, char *buf, int chn); /* 0..7 */
static int no_off(const char *str);
static int read_channel_mask(const char * str);
static int read_field (struct i2c_client *client, u32 awe);
static int write_field (struct i2c_client *client, u8 data, u32 awe);
static int write_adwe(struct i2c_client *client, u32 adwe);
static int write_reg(struct i2c_client *client, u8 reg, u8 val, u8 mask);
static int read_reg(struct i2c_client *client, u8 reg);
static void invalidate_cache(struct i2c_client *client);
static int ltc3589_sysfs_register(struct device *dev);
static void ltc3589_init_of(struct i2c_client *client);
static int ltc3589_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id);
static int ltc3589_i2c_remove(struct i2c_client *client);
int ltc3589_read_field (struct i2c_client *client, u32 awe)
{
return read_field (client, awe);
}
EXPORT_SYMBOL_GPL(ltc3589_read_field);
int ltc3589_write_field (struct i2c_client *client, u8 data, u32 awe)
{
return write_field (client, data, awe);
}
EXPORT_SYMBOL_GPL(ltc3589_write_field);
int ltc3589_write_adwe(struct i2c_client *client, u32 adwe)
{
return write_adwe(client, adwe);
}
EXPORT_SYMBOL_GPL(ltc3589_write_adwe);
void ltc3589_set_simulate(struct i2c_client *client, int simulate)
{
struct ltc3589_data_t *clientdata=i2c_get_clientdata(client);
clientdata->simulate=simulate;
}
EXPORT_SYMBOL_GPL(ltc3589_set_simulate);
/* raw access to i2c registers, need to set address (9 bits) first, then r/w data */
static DEVICE_ATTR(invalidate_cache, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, invalidate_cache_store);
static DEVICE_ATTR(simulate, SYSFS_PERMISSIONS, simulate_show, simulate_store);
static DEVICE_ATTR(address, SYSFS_PERMISSIONS, raw_address_show, raw_address_store);
static DEVICE_ATTR(data, SYSFS_PERMISSIONS, raw_data_show, raw_data_store);
static DEVICE_ATTR(hex_address, SYSFS_PERMISSIONS, raw_hex_address_show,raw_hex_address_store);
static DEVICE_ATTR(hex_data, SYSFS_PERMISSIONS, raw_hex_data_show, raw_hex_data_store);
static DEVICE_ATTR(hex_all, SYSFS_PERMISSIONS & SYSFS_READONLY, raw_hex_all_show, NULL);
static DEVICE_ATTR(hex_adwe, SYSFS_PERMISSIONS, raw_hex_adwe_show, raw_hex_adwe_store);
static DEVICE_ATTR(hex_adwe_help, SYSFS_PERMISSIONS & SYSFS_READONLY, raw_hex_adwe_help_show, NULL);
static struct attribute *raw_dev_attrs[] = {
&dev_attr_invalidate_cache.attr,
&dev_attr_simulate.attr,
&dev_attr_address.attr,
&dev_attr_data.attr,
&dev_attr_hex_address.attr,
&dev_attr_hex_data.attr,
&dev_attr_hex_all.attr,
&dev_attr_hex_adwe.attr,
&dev_attr_hex_adwe_help.attr,
NULL
};
static const struct attribute_group dev_attr_raw_group = {
.attrs = raw_dev_attrs,
.name = "raw",
};
//static ssize_t irq_show (struct device *dev, struct device_attribute *attr, char *buf);
//static ssize_t irq_show_txt (struct device *dev, struct device_attribute *attr, char *buf);
//static ssize_t irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
//static ssize_t pwr_bad_good_show (struct device *dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR(power_good, SYSFS_PERMISSIONS & SYSFS_READONLY, pwr_bad_good_show, NULL);
static DEVICE_ATTR(power_bad, SYSFS_PERMISSIONS & SYSFS_READONLY, pwr_bad_good_show, NULL);
static DEVICE_ATTR(irq, SYSFS_PERMISSIONS, irq_show, irq_store);
static DEVICE_ATTR(irq_txt, SYSFS_PERMISSIONS, irq_show_txt, irq_store);
static DEVICE_ATTR(power_off, SYSFS_PERMISSIONS, power_wait_on_off_show, power_wait_on_off_store);
static DEVICE_ATTR(power_on, SYSFS_PERMISSIONS, power_wait_on_off_show, power_wait_on_off_store);
static DEVICE_ATTR(wait, SYSFS_PERMISSIONS, power_wait_on_off_show, power_wait_on_off_store);
static DEVICE_ATTR(no_wait, SYSFS_PERMISSIONS, power_wait_on_off_show, power_wait_on_off_store);
static DEVICE_ATTR(reference_sel1, SYSFS_PERMISSIONS, reference_select_show, reference_select_store);
static DEVICE_ATTR(reference_sel2, SYSFS_PERMISSIONS, reference_select_show, reference_select_store);
static DEVICE_ATTR(reference_sel_go, SYSFS_PERMISSIONS, reference_select_go_show, reference_select_go_store);
static DEVICE_ATTR(continuous, SYSFS_PERMISSIONS, mode_show, mode_store);
static DEVICE_ATTR(burst, SYSFS_PERMISSIONS, mode_show, mode_store);
static DEVICE_ATTR(pulse_skip, SYSFS_PERMISSIONS, mode_show, mode_store);
static DEVICE_ATTR(pgood_timeout_inhibit, SYSFS_PERMISSIONS, pgood_timeout_inhibit_show, pgood_timeout_inhibit_store);
static struct attribute *control_dev_attrs[] = {
&dev_attr_power_good.attr,
&dev_attr_power_bad.attr,
&dev_attr_irq.attr,
&dev_attr_irq_txt.attr,
&dev_attr_power_off.attr,
&dev_attr_power_on.attr,
&dev_attr_wait.attr,
&dev_attr_no_wait.attr,
&dev_attr_reference_sel1.attr,
&dev_attr_reference_sel2.attr,
&dev_attr_reference_sel_go.attr,
&dev_attr_continuous.attr,
&dev_attr_burst.attr,
&dev_attr_pulse_skip.attr,
&dev_attr_pgood_timeout_inhibit.attr,
NULL
};
static const struct attribute_group dev_attr_control_group = {
.attrs = control_dev_attrs,
.name = "control",
};
//status_fields[]
static int make_status_fields(struct device *dev)
{
int retval=-1;
int index;
struct attribute **pattrs; /* array of pointers to attibutes */
struct device_attribute *dev_attrs;
struct attribute_group *attr_group;
pattrs = devm_kzalloc(dev,(ARRAY_SIZE(status_fields)+1)*sizeof(pattrs[0]), GFP_KERNEL);
if (!pattrs) return -ENOMEM;
dev_attrs = devm_kzalloc(dev, ARRAY_SIZE(status_fields)*sizeof(dev_attrs[0]), GFP_KERNEL);
if (!dev_attrs) return -ENOMEM;
attr_group = devm_kzalloc(dev, sizeof(*attr_group), GFP_KERNEL);
if (!attr_group) return -ENOMEM;
memset(dev_attrs, 0, ARRAY_SIZE(status_fields)*sizeof(dev_attrs[0]));
memset(attr_group, 0, sizeof(*attr_group));
for (index=0;indexname = "status";
attr_group->attrs =pattrs;
dev_dbg(dev,"name=%s, &dev->kobj=0x%08x\n",attr_group->name, (int) (&dev->kobj));
if (&dev->kobj) {
retval = sysfs_create_group(&dev->kobj, attr_group);
}
return retval;
}
static int make_bit_fields(struct device *dev)
{
int retval=-1;
int index;
struct attribute **pattrs; /* array of pointers to attibutes */
struct device_attribute *dev_attrs;
struct attribute_group *attr_group;
pattrs = devm_kzalloc(dev,(ARRAY_SIZE(named_fields)+1)*sizeof(pattrs[0]), GFP_KERNEL);
if (!pattrs) return -ENOMEM;
dev_attrs = devm_kzalloc(dev, ARRAY_SIZE(named_fields)*sizeof(dev_attrs[0]), GFP_KERNEL);
if (!dev_attrs) return -ENOMEM;
attr_group = devm_kzalloc(dev, sizeof(*attr_group), GFP_KERNEL);
if (!attr_group) return -ENOMEM;
memset(dev_attrs, 0, ARRAY_SIZE(named_fields)*sizeof(dev_attrs[0]));
memset(attr_group, 0, sizeof(*attr_group));
for (index=0;indexname = "bit_fields";
attr_group->attrs =pattrs;
dev_dbg(dev,"name=%s, &dev->kobj=0x%08x\n",attr_group->name, (int) (&dev->kobj));
if (&dev->kobj) {
retval = sysfs_create_group(&dev->kobj, attr_group);
}
return retval;
}
static ssize_t invalidate_cache_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
invalidate_cache(client);
return count;
}
static ssize_t simulate_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
return sprintf(buf, "%d\n",clientdata->simulate);
}
static ssize_t simulate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
sscanf(buf, "%du", &clientdata->simulate);
return count;
}
//clientdata->simulate
static ssize_t raw_address_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
return sprintf(buf, "%d\n",clientdata->reg_addr);
}
static ssize_t raw_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
sscanf(buf, "%du", &clientdata->reg_addr);
return count;
}
static ssize_t raw_data_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
int data= read_reg(client, clientdata->reg_addr);
return sprintf(buf, "%d\n",data);
}
static ssize_t raw_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
int data;
sscanf(buf, "%du", &data);
write_reg(client, clientdata->reg_addr, data, 0xff); /* write all register, it is up to user to do R-mod-W */
return count;
}
static ssize_t raw_hex_address_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
return sprintf(buf, "0x%02x\n",clientdata->reg_addr);
}
static ssize_t raw_hex_address_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct ltc3589_data_t *clientdata=i2c_get_clientdata(to_i2c_client(dev));
sscanf(buf, "%x", &clientdata->reg_addr);
return count;
}
static ssize_t raw_hex_data_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
int data= read_reg(client, clientdata->reg_addr);
return sprintf(buf, "0x%02x\n",data);
}
static ssize_t raw_hex_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
int data;
sscanf(buf, "%x", &data);
write_reg(client, clientdata->reg_addr, data, 0xff); /* write all register, it is up to user to do R-mod-W */
return count;
}
static ssize_t raw_hex_all_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int low_addr=0,reg,data,rc,len=0, count=PAGE_SIZE;
struct i2c_client *client = to_i2c_client(dev);
// struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
for (reg=low_addr;reg<=LAST_REG;reg++) if (count>10){
if ((reg & 0xf) ==0){
rc=sprintf(buf, "%02x: ",reg);
buf+=rc;
len+=rc;
count-=rc;
}
data= read_reg(client, reg); //ignore errors
if (data<0) rc=sprintf(buf, "??");
else rc=sprintf(buf, "%02x",data);
buf+=rc;
len+=rc;
count-=rc;
if (((reg & 0xf) == 0xf) || (reg==LAST_REG)){
rc=sprintf(buf, "\n");
} else {
rc=sprintf(buf, " ");
}
buf+=rc;
len+=rc;
count-=rc;
}
return len;
}
static ssize_t raw_hex_adwe_help_show (struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Setting one/multiple registers with masks in the form [0x]AADDWW, where AA is register address\n" \
"DD - data byte and WW - write enable bits ( 1 - write, 0 - keep old)\n" \
"When read, provides current register data that can be used in device tree.\n");
}
//static const u32 register_masks[]= {
static ssize_t raw_hex_adwe_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int i,data;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
for (i=0;i>8)))<0) return data;
buf+=sprintf(buf," 0x%x",((register_masks[i] & 0x1ff00)<<8) | (register_masks[i] & 0xff) | ((data & 0xff)<<8));
if (((i+1) & 0x7)==0) buf+=sprintf(buf,"\n");
}
buf+=sprintf(buf,"\n");
return buf-cp;
}
/*
* accepts single or multiple data, each [0x]AAADDWW - AAA - register address, DD - data byte, WW - write enable mask (1 - write, 0 - keep).
* Ignores any other characters, so same format as in dts with hex data is OK
*/
static ssize_t raw_hex_adwe_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
const char hex_digits[]="0123456789abcdefABCDEF";
struct i2c_client *client = to_i2c_client(dev);
struct ltc3589_data_t *clientdata= i2c_get_clientdata(client);
int adwe,rc=0;
int left=count,num_bytes;
char * cp;
mutex_lock(&clientdata->lock);
while ((left>0) && ((cp=strpbrk(buf,hex_digits))) && cp[0]){
left -= (cp-buf);
buf = cp;
dev_dbg(dev,"left=%d", left);
sscanf(buf, "%x%n", &adwe,&num_bytes);
left-=num_bytes;
buf+=num_bytes;
dev_dbg(dev,"left=%d num_bytes=%d, adwe=0x%08x", left,num_bytes,adwe);
if (((rc=write_adwe(client, adwe)))<0) {
mutex_unlock(&clientdata->lock);
return rc;
}
}
mutex_unlock(&clientdata->lock);
return count;
}
//static const char * chn_names[]={"SD1","SD2","SD3","BB","LDO1","LDO2","LDO3","LDO4"};
//static const char * pwr_states[]={"power_off","power_on"};
//static const char * wait_states[]={"no_wait", "wait"};
static ssize_t power_wait_on_off_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int rc,i,invert;
char * cp=buf;
u32 awe;
struct i2c_client *client = to_i2c_client(dev);
awe= ((strcmp(attr->attr.name,wait_states[0])==0) || (strcmp(attr->attr.name,wait_states[1])==0))?LTC3589_AWE_SCR2:LTC3589_AWE_OVEN;
invert=((strcmp(attr->attr.name,pwr_states[0])==0) || (strcmp(attr->attr.name,wait_states[0])==0))?0xff:0;
if (((rc=read_field(client,awe)))<0) return rc;
rc=((rc & 0xf) | 0x10 | ((rc & 0x70)<<1)) ^ invert;
for (i=0;i<8;i++) if (rc & (1<attr.name,wait_states[0])==0) || (strcmp(attr->attr.name,wait_states[1])==0))?LTC3589_AWE_SCR2:LTC3589_AWE_OVEN;
data=((strcmp(attr->attr.name,pwr_states[0])==0) || (strcmp(attr->attr.name,wait_states[0])==0))?0:0xff;
mask=read_channel_mask(buf);
mask=(mask & 0xf) | ((mask >> 1) & 0x70);
awe = (awe & 0xff00) | mask;
if (((rc=write_field (client, data, awe)))<0) return rc;
return count;
}
static ssize_t pgood_timeout_inhibit_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int rc;
struct i2c_client *client = to_i2c_client(dev);
if (((rc=read_field(client,LTC3589_AWE_SCR2_PGOOD_SHTDN_INH)))<0) return rc;
return sprintf(buf, "%d\n",rc);
}
static ssize_t pgood_timeout_inhibit_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc,data;
struct i2c_client *client = to_i2c_client(dev);
sscanf(buf, "%d", &data);
if (((rc=write_field(client,data?1:0,LTC3589_AWE_SCR2_PGOOD_SHTDN_INH)))<0) return rc;
return count;
}
static ssize_t mode_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int rc,m,i;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
if (((rc=read_field(client,LTC3589_AWE_SCR1)))<0) return rc;
for (m=0;mattr.name,modes[m])==0) break;
if (m>=ARRAY_SIZE(modes)) return -EINVAL;
for (i=0;i<4;i++) if (((rc>>(2*i)) & 3) == m) {
if (buf!=cp) buf+=sprintf(buf," ");
buf+=sprintf(buf,"%s",chn_names[i]);
}
buf+=sprintf(buf,"\n");
return buf-cp;
}
static ssize_t mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc,mask,m,data;
u32 awe;
struct i2c_client *client = to_i2c_client(dev);
for (m=0;mattr.name,modes[m])==0) break;
if (m>=ARRAY_SIZE(modes)) return -EINVAL;
mask=read_channel_mask(buf);
mask=((mask & 1)? 3:0) | ((mask & 2)? 0xc:0) | ((mask & 4)? 0x30:0) | ((mask & 8)? 0x40:0);
awe = (LTC3589_AWE_SCR1 & 0xff00) | mask;
data= m | (m<<2) | (m<<4) | (m<<6);
if (((rc=write_field (client, data, awe)))<0) return rc;
return count;
}
static ssize_t reference_select_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int rc,m,i,chn;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
if (((rc=read_field(client,LTC3589_AWE_VCCR)))<0) return rc;
for (m=0;mattr.name,reference_sel[m])==0) break;
if (m>=ARRAY_SIZE(reference_sel)) return -EINVAL;
for (i=0;i<4;i++) if (((rc>>(2*i+1)) & 1)==m){
chn=i;
if (i==3) chn=5 ; /* LDO2 */
if (buf!=cp) buf+=sprintf(buf," ");
buf+=sprintf(buf,"%s",chn_names[chn]);
/* if ((rc>>(2*i)) & 1){
buf+=sprintf(buf,"(slewing)");
}
*/
}
buf+=sprintf(buf,"\n");
return buf-cp;
}
/* not raising go */
static ssize_t reference_select_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc,mask,m,data;
u32 awe;
struct i2c_client *client = to_i2c_client(dev);
for (m=0;mattr.name,reference_sel[m])==0) break;
if (m>=ARRAY_SIZE(reference_sel)) return -EINVAL;
mask=read_channel_mask(buf);
mask=((mask & 1)? 2:0) | ((mask & 2)? 0x8:0) | ((mask & 4)? 0x20:0) | ((mask & 0x20)? 0x80:0);
awe = (LTC3589_AWE_VCCR & 0xff00) | mask;
data= m?0xff:0;
if (((rc=write_field (client, data, awe)))<0) return rc;
return count;
}
static ssize_t reference_select_go_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int rc,i,chn;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
if (((rc=read_field(client,LTC3589_AWE_VCCR)))<0) return rc;
for (i=0;i<4;i++) if ((rc>>(2*i)) & 1){
chn=i;
if (i==3) chn=5 ; /* LDO2 */
if (buf!=cp) buf+=sprintf(buf," ");
buf+=sprintf(buf,"%s",chn_names[chn]);
}
buf+=sprintf(buf,"\n");
return buf-cp;
}
static ssize_t reference_select_go_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc,mask,data;
u32 awe;
struct i2c_client *client = to_i2c_client(dev);
mask=read_channel_mask(buf);
mask=((mask & 1)? 1:0) | ((mask & 2)? 0x4:0) | ((mask & 4)? 0x10:0) | ((mask & 0x20)? 0x40:0);
awe = (LTC3589_AWE_VCCR & 0xff00) | mask;
data= 0xff;
if (((rc=write_field (client, data, awe)))<0) return rc;
return count;
}
//--------------------
static ssize_t field_show (struct device *dev, struct device_attribute *attr, char *buf)
{
return get_field_value (dev, attr->attr.name, buf, 1); /* with newline */
}
static ssize_t field_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc,i;
int data;
u32 awe;
struct i2c_client *client = to_i2c_client(dev);
for (i=0;iattr.name,named_fields[i].name)==0) {
awe=named_fields[i].awe;
dev_dbg(dev,"i=%d, field name=%s awe=0x%04x\n", i, named_fields[i].name, (int) awe);
break;
}
if (i>=ARRAY_SIZE(named_fields)) {
for (i=0;iattr.name,status_fields[i].name)==0) {
awe=status_fields[i].awe;
dev_dbg(dev,"i=%d, status field name=%s awe=0x%04x\n", i, status_fields[i].name, (int) awe);
break;
}
if (i>=ARRAY_SIZE(status_fields)) return -EINVAL;
}
sscanf(buf, "%du", &data);
if (((rc=write_field (client, data, awe)))<0) return rc;
return count;
}
static ssize_t get_field_value (struct device *dev, const char* name, char *buf, int newline)
{
int rc,i;
char * cp=buf;
u32 awe;
struct i2c_client *client = to_i2c_client(dev);
for (i=0;i=ARRAY_SIZE(named_fields)) {
for (i=0;i=ARRAY_SIZE(status_fields)) return -EINVAL;
}
if (((rc=read_field(client,awe)))<0) return rc;
buf+=sprintf(buf,"%d",rc);
if (newline) buf+=sprintf(buf,"\n");
return buf-cp;
}
static ssize_t irq_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int rc;
struct i2c_client *client = to_i2c_client(dev);
if (((rc=read_field(client,LTC3589_AWE_IRQSTAT)))<0) return rc;
return sprintf(buf,"%d\n",rc);
}
static ssize_t irq_show_txt (struct device *dev, struct device_attribute *attr, char *buf)
{
int rc;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
if (((rc=read_field(client,LTC3589_AWE_IRQSTAT)))<0) return rc;
buf += sprintf(buf,"0x%02x",rc);
if (rc & LTC3589_AWE_IRQSTAT_PGOOD_TIMOUT) buf += sprintf(buf,"PGOOD timeout");
if (rc & LTC3589_AWE_IRQSTAT_NEAR_UV) buf += sprintf(buf,", Near undervoltage");
if (rc & LTC3589_AWE_IRQSTAT_HARD_UV) buf += sprintf(buf,", Hard undervoltage");
if (rc & LTC3589_AWE_IRQSTAT_NEAR_THERM) buf += sprintf(buf,", Near undervoltage");
if (rc & LTC3589_AWE_IRQSTAT_HARD_THERM) buf += sprintf(buf,", Hard undervoltage");
buf += sprintf(buf,"\n");
return buf-cp;
}
static ssize_t irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int rc;
struct i2c_client *client = to_i2c_client(dev);
if (((rc=write_field (client, 0, LTC3589_AWE_CLIRQ)))<0) return rc;
return count;
}
static ssize_t pwr_bad_good_show (struct device *dev, struct device_attribute *attr, char *buf)
{
int rc,i,pg;
char * cp=buf;
struct i2c_client *client = to_i2c_client(dev);
if (((rc=read_field(client,LTC3589_AWE_PGSTAT)))<0) return rc;
pg=(rc & 0xe0) | ((rc>>1) & 0xff) | ((rc << 4) & 0x10);
if (strstr(attr->attr.name,"bad")){
pg^=0xff;
}
for (i=0;i<8;i++) if (pg & (1<3)) return 0;
if (((rc=read_field(client,LTC3589_AWE_SCR1)))<0) return rc;
return sprintf (buf,"%s",modes[(rc >> (chn<<1)) &3]);
}
static int get_chn_pwr(struct device *dev, char *buf, int chn) /* 0..7 */
{
int rc;
struct i2c_client *client = to_i2c_client(dev);
if ((chn<0) || (chn>7)) return 0;
if (((rc=read_field(client,LTC3589_AWE_OVEN)))<0) return rc;
rc=(rc & 0xf) | 0x10 | ((rc & 0x70)<<1);
return sprintf (buf,"%s",pwr_states[(rc >> chn) & 1]);
}
static int get_chn_wait(struct device *dev, char *buf, int chn) /* 0..7 */
{
int rc;
struct i2c_client *client = to_i2c_client(dev);
if ((chn<0) || (chn>7)) return 0;
if (((rc=read_field(client,LTC3589_AWE_SCR2)))<0) return rc;
rc=(rc & 0xf) | 0x10 | ((rc & 0x70)<<1);
return sprintf (buf,"%s",wait_states[(rc >> chn) & 1]);
}
static int get_ref_sel_go(struct device *dev, char *buf, int chn) /* 0..7 */
{
int rc;
struct i2c_client *client = to_i2c_client(dev);
if ((chn<0) || ((chn>2) && (chn!=5))) return 0;
if (chn==5) chn=3;
if (((rc=read_field(client,LTC3589_AWE_VCCR)))<0) return rc;
rc =(rc >> (2*chn)) & 3;
return sprintf (buf,"%s%s",reference_sel[(rc>>1)&1],(rc&1)?" (slewing)":"");
}
static int no_off(const char *str)
{
return strstr(str,"_off")?0:1;
}
static int read_channel_mask(const char * str)
{
int mask =0, i;
for (i=0;i>8;
mask=awe&0xff;
if (mask!=0){
nshift=0;
while (((1<> nshift;
}
return 0;
}
static int write_field (struct i2c_client *client, u8 data, u32 awe)
{
int rc,nshift;
u8 mask,reg_data;
u8 reg;
reg=awe>>8;
mask=awe&0xff;
if (mask!=0){
nshift=0;
while (((1<>8) & 0xff;
u16 reg= (adwe>>16) & 0xff;
return write_reg(client, reg, data, we);
}
static int write_reg(struct i2c_client *client, u8 reg, u8 val, u8 mask)
{
int rc;
struct ltc3589_data_t *clientdata = i2c_get_clientdata(client);
if (reg>LAST_REG) return -EINVAL;
if (mask==0) return 0;
dev_dbg(&client->dev,"reg=0x%x, val=0x%x, mask=0x%x\n", (int) reg, (int) val, (int) mask);
if (mask !=0xff){
if (((rc=read_reg(client, reg)))<0) return rc;
val=((val ^ rc) & mask)^ rc;
if ((val==rc) && !(clientdata->cache[reg].flags & CACHE_VOLAT)) {
dev_dbg(&client->dev,"No change and not volatile -> no write\n");
return 0;
}
}
clientdata->cache[reg].data= val;
clientdata->cache[reg].flags |= CACHE_INIT;
if (clientdata->simulate){
dev_info(&client->dev,">>> Simulating LTC3589 register write: 0x%02x->[0x%02x]\n",(int) reg, (int) val);
return 0;
}
return i2c_smbus_write_byte_data(client, reg, val);
}
static int read_reg(struct i2c_client *client, u8 reg)
{
int rc;
struct ltc3589_data_t *clientdata = i2c_get_clientdata(client);
if (reg>LAST_REG) return -EINVAL;
if (clientdata && (clientdata->cache[reg].flags & CACHE_INIT) && !(clientdata->cache[reg].flags & CACHE_VOLAT)){
dev_dbg(&client->dev,"Using cached register: reg=0x%x -> 0x%x\n",reg,(int) clientdata->cache[reg].data);
return clientdata->cache[reg].data;
}
rc= i2c_smbus_read_byte_data(client, reg & 0xff);
dev_dbg(&client->dev,"reading i2c device : slave=0x%x, reg=0x%x -> 0x%x\n",(int) (client->addr),reg,rc);
if (rc<0) return rc;
if (clientdata){
clientdata->cache[reg].data= (u8) rc;
clientdata->cache[reg].flags |= CACHE_INIT;
}
return rc;
}
static void invalidate_cache(struct i2c_client *client)
{
int i;
struct ltc3589_data_t *clientdata = i2c_get_clientdata(client);
for (i=0;i<=LAST_REG;i++){
clientdata->cache[i].flags&= ~CACHE_INIT;
}
}
static int ltc3589_sysfs_register(struct device *dev)
{
int retval=0;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_raw_group)))<0) return retval;
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_control_group)))<0) return retval;
if (((retval = make_status_fields (dev)))<0) return retval;
if (((retval = make_bit_fields (dev)))<0) return retval;
}
return retval;
}
static void ltc3589_init_of(struct i2c_client *client)
{
// struct device *dev=&client->dev;
const __be32 * config_data;
const char * init_type_string;
int init_type=0; /* 0 - none, 1 - always, 2 - if not running (TODO) */
struct device_node *node = client->dev.of_node;
int len,i,n;
char buf[40];
struct ltc3589_setup_data {
u8 page;
u8 reg;
u8 data;
u8 mask;
};
struct ltc3589_setup_data setup_data;
__be32 * setup_data_be32= (__be32 *) &setup_data;
/* add stuff */
#if 0
if (node) {
init_type_string = of_get_property(client->dev.of_node, "ltc3589,init", &len);
if (init_type_string){
if (strcmp(init_type_string,"always")==0) init_type=1;
else if (strcmp(init_type_string,"if off")==0) init_type=2;
else {
dev_err(&client->dev,"Unrecognized ltc3589 initialization type '%s'. Only 'always' and 'if off' are permitted\n",init_type_string);
}
}
switch (init_type){
case 2:
// static int is_set_up(struct i2c_client *client);
i=is_set_up(client);
if (i<0){
dev_err(&client->dev,"Error reading i2c register, aborting initialization\n");
return;
} else if (i>0){
init_type=0;
dev_dbg(&client->dev,"Skipping conditional initialization (some driver variables will not be initialized)\n");
return;
}
init_type=1;
/* falling to initialization */
case 1:
pre_init(client,1); // clear outputs and muxes - they will be programmed later
break;
}
config_data = of_get_property(client->dev.of_node, "ltc3589,configuration_data", &len);
if (config_data){
len /= sizeof(*config_data);
dev_dbg(&client->dev,"Read %d values\n",len);
dev_dbg(&client->dev,"Found %d items in 'ltc3589,configuration_data' in the Device Tree\n",len);
for (i=0;idev,"page_reg=0x%03x, data=0x%02x, mask=0x%02x \n",
(int) page_reg,(int)setup_data.data,(int)setup_data.mask);
if (write_reg(client, page_reg, setup_data.data, setup_data.mask)<0) return;
}
}
/* input section */
/* setting input frequency here divides (if needed) and feeds it to the PLL reference. Other variants can use raw register writes */
for (n=0;in_freq_names[n];n++){
sprintf(buf,"ltc3589,%s",in_freq_names[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data && (len>0)){
dev_dbg(&client->dev,"Found '%s', value = %d (0x%x)\n",buf,(int)(be32_to_cpup(config_data)),(int)(be32_to_cpup(config_data)));
if (set_in_frequency(client, be32_to_cpup(config_data),n)<0) return; /* 32 bits are sufficient here */
}
}
/* setting PLL for the most important output frequency, sets analog parameters accordingly. Assumes input frequency set above */
for (n=0;pll_setup_names[n];n++){
sprintf(buf,"ltc3589,%s",pll_setup_names[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data && (len>0)){
len /= sizeof(*config_data);
freq[0]=be32_to_cpup(config_data);
if (len<3){
freq[1]=0;
freq[2]=1;
} else {
freq[1]=be32_to_cpup(&config_data[1]);
freq[2]=be32_to_cpup(&config_data[2]);
}
dev_dbg(&client->dev,"Found '%s', value = %lld+(%lld/%lld)\n",buf,freq[0],freq[1],freq[2]);
if (n & 2){ /* by output */
if (set_pll_freq_by_out(client, freq, n & 1)<0) return;
} else { /* directly set PLL frequency */
if (set_pll_freq (client, freq, n & 1)<0) return;
}
if (set_pll_paremeters(client)<0) return;
/* if (set_misc_registers(client)<0) return; */ /* moved to pre_init() */
}
}
/* setting MSn dividers (same channel as output), powering them up, setting output dividers and routing outputs */
for (n=0;out_freq_setup_names[n];n++){
sprintf(buf,"ltc3589,%s",out_freq_setup_names[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data && (len>0)){
len /= sizeof(*config_data);
freq[0]=be32_to_cpup(config_data);
if (len<3){
freq[1]=0;
freq[2]=1;
} else {
freq[1]=be32_to_cpup(&config_data[1]);
freq[2]=be32_to_cpup(&config_data[2]);
}
dev_dbg(&client->dev,"Found '%s', value = %lld+(%lld/%lld)\n",buf,freq[0],freq[1],freq[2]);
if (set_out_frequency_and_route(client, freq, n&3, n>>2)<0) return;
}
}
/* configure output driver standard */
for (n=0;drv_configs[n].description;n++){
sprintf(buf,"ltc3589,%s",drv_configs[n].description);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data){
len /= sizeof(*config_data);
for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
if (configure_output_driver(&client->dev, drv_configs[n].description, setup_data.mask)<0) return;
}
}
}
/* configure disabled state of the output(s) */
for (n=0;out_dis_states[n];n++){
sprintf(buf,"ltc3589,%s",out_dis_states[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data){
len /= sizeof(*config_data);
for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
if (set_drv_disable(client, n, setup_data.mask)<0) return;
}
}
}
/* configure powerdown state of the output(s) */
for (n=0;out_pwr_states[n];n++){
sprintf(buf,"ltc3589,%s",out_pwr_states[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data){
len /= sizeof(*config_data);
for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
if (set_drv_powerdown(client, n, setup_data.mask)<0) return;
}
}
}
/* configure output enable state of the output(s) */
for (n=0;out_en_states[n];n++){
sprintf(buf,"ltc3589,%s",out_en_states[n]);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data){
len /= sizeof(*config_data);
for (i=0;idev,"Setting '%s', channel %d",buf,setup_data.mask);
if (set_drv_disable(client, n, setup_data.mask)<0) return;
}
}
}
/* setting spread spectrum parameters */
for (n=0;n<4;n++){
sprintf(buf,"ltc3589,spread_spectrum_%d",n);
config_data = of_get_property(client->dev.of_node, buf, &len);
if (config_data && (len>0)){
len /= sizeof(*config_data);
rate=get_ss_down_rate(client, n);
amp= get_ss_down_amplitude(client, n);
if (len>1) amp = be32_to_cpup(&config_data[1]);
if (len>2) rate = be32_to_cpup(&config_data[2]);
if (store_ss_down_parameters(client, rate, amp, n)==0){
dev_dbg(&client->dev,"Set spread spectrum parameters for MS%d, amplitude=%d (*0.01%%), rate=%d Hz, %s\n",
n,amp,rate,config_data[0]?"ON":"OFF");
} else {
dev_err(&client->dev,"Failed to set spread spectrum parameters for MS%d, amplitude=%d (*0.01%%), rate=%d Hz, %s\n",
n,amp,rate,config_data[0]?"ON":"OFF");
continue;
}
if (config_data[0]){ /* enable SS */
if ((set_ss_down(client, n)==0) && /* calculate and set SS registers */
(set_ss_state(client, 1, n)==0)){ // enable SS. Not using enable_spread_spectrum() as we'll reset MS later anyway
dev_dbg(&client->dev,"Spread spectrum enabled for MS%d\n",n);
} else {
dev_dbg(&client->dev,"Fail to enable spread spectrum for MS%d\n",n);
}
}
}
}
} else {
dev_info(&client->dev,"Device tree data not found for %s\n",client->name);
}
if (init_type){
if (post_init(client,INIT_TIMEOUT)<0) dev_err(&client->dev,"ltc3589 initialization failed\n");
else dev_info(&client->dev,"ltc3589 initialized\n");
}
#endif
}
static int ltc3589_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int i,rc=0;
struct ltc3589_data_t *clientdata = NULL;
/* initialize i2c ... */
if (read_field (client, LTC3589_AWE_IRQSTAT_PGOOD_TIMOUT)<0) {
dev_err(&client->dev, "%s: chip not detected\n",id->name);
return -EIO;
}
dev_info(&client->dev,
"Chip %s is found, driver version %s\n", id->name, DRV_VERSION);
clientdata = devm_kzalloc(&client->dev, sizeof(*clientdata), GFP_KERNEL);
for (i=0;i<=LAST_REG;i++){
clientdata->cache[i].flags=0;
clientdata->cache[i].data=0;
}
for (i=0;volatile_registers[i]>=0;i++){
clientdata->cache[volatile_registers[i]>>8].flags |= CACHE_VOLAT;
}
clientdata->simulate=0;
clientdata->reg_addr=0;
//volatile_registers[]
i2c_set_clientdata(client, clientdata);
ltc3589_sysfs_register(&client->dev);
mutex_init(&clientdata->lock);
ltc3589_init_of(client);
return 0;
}
static int ltc3589_i2c_remove(struct i2c_client *client)
{
return 0;
}
static struct i2c_driver ltc3589_i2c_driver = {
.driver = {
.name = "ltc3589",
.owner = THIS_MODULE,
},
.probe = ltc3589_i2c_probe,
.remove = ltc3589_i2c_remove,
.id_table = ltc3589_id,
};
module_i2c_driver(ltc3589_i2c_driver);
MODULE_DEVICE_TABLE(i2c, ltc3589_id);
MODULE_AUTHOR("Andrey Filippov ");
MODULE_DESCRIPTION("LTC3589 I2C bus driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("i2c:ltc3589");
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/drivers/misc/vsc330x.c 0000664 0000000 0000000 00000145023 12666203034 0025761 0 ustar 00root root 0000000 0000000 /*!***************************************************************************
*! FILE NAME : vsc330x.c
*! DESCRIPTION: control of the VSC3304 4x4 crosspoint switch
*! Copyright (C) 2013 Elphel, 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 3 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#define DRV_VERSION "1.0"
/* TODO: Descriptions from vsc3312 - check differences */
#define I2C_PAGE_CONNECTION 0x00 /* When written to I2C_CURRENT_PAGE, makes registers 0..0xf control corresponding output (0..0xf) source
(input number) bit 4 (+0x10) - turn output off, bits 3:0 - source */
#define I2C_PAGE_INPUT_ISE 0x10 /* When written to I2C_CURRENT_PAGE, makes registers 0..0xf control corresponding input (0..0xf)
ISE (equalization): Bits 5:4 ISE short: 0 - off, 1 - minimal, 2 - moderate, 3 - maximal;
bits 3:2 ISE medium, bits 1:0 ISE Long time constant */
#define I2C_PAGE_INPUT_STATE 0x11 /* When written to I2C_CURRENT_PAGE, makes registers 0..0xf control corresponding input (0..0xf) enable,
polarity and termination (default 6)
Bit 2 (+4) Terminate to VDD ( 0 - connect, 1 - do not connect) - dedicated (0..7) inputs only
Bit 1 (+2) Input power (0 - on, 1 - off)
Bit 0 (+1) Invert signal at input */
#define I2C_INPUT_STATE_DATA 0x04 /* terminated,enabled, not inverted */
#define I2C_PAGE_INPUT_LOS 0x12 /* When written to I2C_CURRENT_PAGE, makes registers 0..0xf control corresponding input (0..0xf)
LOS (loss of signal) threshold
Bits 2:0 - level in mV for dedicated(bidirectional) inputs: 0,1,6,7 - unused, 2 - 150(170),
3 - 200(230), 4 - 250(280), 5 - 300(330) */
#define I2C_PAGE_OUTPUT_PRE_LONG 0x20 /* When written to I2C_CURRENT_PAGE, makes registers 0..0xf control corresponding output (0..0xf)
long time constant pre-emphasis
Bits 6:3 Pre-Emphasis level (0x0 - off, 0x1 - min, 0xf - max - 0..6dB), bits 2:0 - Pre-emphasis
decay (0x0 - fastest, 0x7 - slowest) in 500..1500 ps range */
#define I2C_PAGE_OUTPUT_PRE_SHORT 0x21 /* When written to I2C_CURRENT_PAGE, makes registers 0..0xf control corresponding output (0..0xf)
short time constant pre-emphasis
Bits 6:3 Pre-Emphasis level (0x0 - off, 0x1 - min, 0xf - max - 0..6dB),
bits 2:0 - Pre-emphasis decay (0x0 - fastest, 0x7 - slowest) in 30..500 ps range */
#define I2C_PAGE_OUTPUT_LEVEL 0x22 /* When written to I2C_CURRENT_PAGE, makes registers 0..0xf control corresponding output (0..0xf)
short time constant pre-emphasis
Bits 3:0 - peak-to-peak 0,1,0xe,0xf - unused, 0x2-405mV,0x3-425V,0x4-455mV,0x5-485mV,0x6-520mV,
0x7-555mV,0x8-605mV,0x9-655mV,0xa-720mV,0xb-790mV,0xc-890mV,0xd-990mV (+3.3VDC required)
bit 4 (+0x10) - for 8-15 used as inputs only: terminate inputs 8..15 to VDDIO-0.7V */
#define I2C_PAGE_OUTPUT_STATE 0x23 /* When written to I2C_CURRENT_PAGE, makes registers 0..0xf control corresponding output (0..0xf)
OOB signaling and output polarity
bits 4:1 - operation mode: 0xa - inverted, 0x5 - normal, 0x0 - suppressed
bit 0 - OOB control: 1 - enable LOS forwarding, 0 - ignore LOS */
#define I2C_PAGE_CHANNEL_STATUS 0xf0 /* When written to I2C_CURRENT_PAGE, makes registers 0..0xf monitor corresponding input (0..0xf) LOS status
bit 0 - LOS status: 1 - LOS detected (loss of signal), 0 - signal present (input has to be enabled,
otherwise 0 is read)when reading from address 0x10 of this page:
bit 0 - value of STAT0
bit 1 - value of STAT1 */
#define I2C_PAGE_STATUS0_CONFIGURE 0x80 /* When written to I2C_CURRENT_PAGE, makes registers 0..0xf control selected input LOS to be OR-ed
to STAT0 output pin (and bit)
bit 0 : 1 - OR this input channel LOS status to STAT0 */
#define I2C_PAGE_STATUS1_CONFIGURE 0x81 /* When written to I2C_CURRENT_PAGE, makes registers 0..0xf control selected input LOS to be OR-ed
to STAT1 output pin (and bit)
bit 0 : 1 - OR this input channel LOS status to STAT1 */
#define I2C_PAGE_STATUS_READ 0xf0 /* Read only from reg=0x10: bit 0 - status0, bit 1 - status 1 */
#define I2C_GLOBAL_CONNECTION 0x50 /* Bit 4 (+0x10) - disable all outputs, bits 3:0 - input number to connect to all outputs */
#define I2C_GLOBAL_INPUT_ISE 0x51 /* Bits 5:4 ISE short: 0 - off, 1 - minimal, 2 - moderate, 3 - maximal; bits 3:2 ISE medium,
bits 1:0 ISE Long time constant */
#define I2C_GLOBAL_INPUT_STATE 0x52 /* Bit 2 (+4) - terminate input to VDD (0..7 only) 0-connect, 1 Normal;
Bit 1 (+2) Input power off (0 - On, 1 - Off) bit0 (+1): Input polarity: 1 - inverted, 0 - normal */
#define I2C_GLOBAL_INPUT_LOS 0x53 /* Bits 2:0 - level in mV for dedicated(bidirectional) inputs: 0,1,6,7 - unused, 2 - 150(170),
3 - 200(230), 4 - 250(280), 5 - 300(330) */
#define I2C_GLOBAL_OUTPUT_PRE_LONG 0x54 /* Bits 6:3 Pre-Emphasis level (0x0 - off, 0x1 - min, 0xf - max - 0..6dB),
bits 2:0 - Pre-emphasis decay (0x0 - fastest, 0x7 - slowest) in 500..1500 ps range */
#define I2C_GLOBAL_OUTPUT_PRE_SHORT 0x55 /* Bits 6:3 Pre-Emphasis level (0x0 - off, 0x1 - min, 0xf - max - 0..6dB),
bits 2:0 - Pre-emphasis decay (0x0 - fastest, 0x7 - slowest) in 30..500 ps range */
#define I2C_GLOBAL_OUTPUT_LEVEL 0x56 /* Bits 3:0 - peak-to-peak 0,1,0xe,0xf - unused,0x2-405mV,0x3-425V,0x4-455mV,0x5-485mV,
0x6-520mV,0x7-555mV,0x8-605mV,0x9-655mV,0xa-720mV,0xb-790mV,0xc-890mV,0xd-990mV (+3.3VDC required)
bit 4 (+0x10) terminate inputs 8..15 to VDDIO-0.7V */
#define I2C_GLOBAL_OUTPUT_STATE 0x57 /* +1 (bit 0) - LOS, +0x15 - inverted, 0xa0 - normal, +0 - "Common mode" ? */
#define I2C_GLOBAL_OUTPUT_STATE_DATA 0x0b /* No inversion, enable OOB forwarding on all channels */
#define I2C_GLOBAL_STATUS0 0x58 /* Bit 0 - selected for Status0 chanel LOS on from all channels */
#define I2C_GLOBAL_STATUS1 0x59 /* Bit 0 - selected for Status1 chanel LOS on from all channels */
#define I2C_CORE_CONFIGURATION 0x75
#define I2C_CORE_CONFIGURATION_DATA 0x18 /* default 0x18 - 0x10 - leftEn, 0x8 - rightEn, 0x4 - DNU, 0x2 - BufferForceOn, 0x1 - Config polarity */
#define I2C_CORE_CONFIGURATION_DATAF 0x19 /* default with inverted Config polarity (freeze update) */
#define I2C_SLAVE_ADDRESS 0x78 /* programmed only, not hardwired */
#define I2C_INTERFACE_MODE 0x79
#define I2C_INTERFACE_MODE_DATA 0x02 /* i2c (1 - 4-wire) */
#define I2C_SOFTWARE_RESET 0x7a
//#define I2C_SOFTWARE_RESET_DATA 0x10 /* to reset, 0 - normal */ not used - but number 4 is used instead
#define I2C_CURRENT_PAGE 0x7f
#define PORT_PEFIX "port_"
#define ALL_PORTS "all"
#define MAX_PORTS 16
#define I2C_PAGE_GLOBAL -1 /* does not use paging access */
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
static const char port_names[][8]={
PORT_PEFIX "00",PORT_PEFIX "01",PORT_PEFIX "02",PORT_PEFIX "03",PORT_PEFIX "04",
PORT_PEFIX "05",PORT_PEFIX "06",PORT_PEFIX "07",PORT_PEFIX "08",PORT_PEFIX "09",
PORT_PEFIX "10",PORT_PEFIX "11",PORT_PEFIX "12",PORT_PEFIX "13",PORT_PEFIX "14",
PORT_PEFIX "15",PORT_PEFIX "16",PORT_PEFIX "17",PORT_PEFIX "18",PORT_PEFIX "19",
PORT_PEFIX "20",PORT_PEFIX "21",PORT_PEFIX "22",PORT_PEFIX "23",PORT_PEFIX "24",
PORT_PEFIX "25",PORT_PEFIX "26",PORT_PEFIX "27",PORT_PEFIX "28",PORT_PEFIX "29",
PORT_PEFIX "30",PORT_PEFIX "31"};
static const struct i2c_device_id vsc330x_id[] = {
{ "vsc3304", 0 },
{ "vsc3308", 1 },
{ "vsc3312", 2 },
{ }
};
MODULE_DEVICE_TABLE(i2c, vsc3304_id);
struct vsc330x_data_t {
int address_mode_data; // vsc3304 needs 6 to be written, otherwise weird modification of bit 3
int last_page;
u32 in_ports;
u32 out_ports;
};
static const struct vsc330x_data_t vsc330x_data[] = {
{.address_mode_data=6,
.in_ports=0xff00, .out_ports=0xff00}, /* 3304 - all ports I/O shared*/
{.address_mode_data=-1, // No data, unknown if it is needed for 3308
.in_ports=0x00ff, .out_ports=0x00ff}, /* 3308 - no ports I/O shared */
{.address_mode_data=-1, // No data, unknown if it is needed for 3308
.in_ports=0xffff, .out_ports=0xffff} /* 3312 - some ports I/O shared*/
};
static int init_device(struct i2c_client *client);
static int read_reg(struct i2c_client *client, u8 reg);
static int write_reg(struct i2c_client *client, u8 reg, u8 val);
static int read_field(struct i2c_client *client, u8 reg, int ls_bit_num, int width);
static int read_page_field(struct i2c_client *client, int page, u8 reg, int ls_bit_num, int width);
static int write_field(struct i2c_client *client, u8 reg, u8 val, int ls_bit_num, int width);
static int write_page_field(struct i2c_client *client, int page, u8 reg, u8 val, int ls_bit_num, int width);
static ssize_t field_show(struct device *dev, struct device_attribute *attr, char *buf,
int page, int ls_bit_num, int width);
static ssize_t field_show_reg(struct device *dev, char *buf, int page, int reg, int ls_bit_num, int width);
static ssize_t field_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count,
int page, int ls_bit_num, int width);
static ssize_t field_store_reg(struct device *dev, const char *buf, size_t count,
int page, int reg, int ls_bit_num, int width);
static ssize_t connection_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t connection_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t input_ISE_short_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t input_ISE_short_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t input_ISE_medium_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t input_ISE_medium_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t input_ISE_long_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t input_ISE_long_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t input_state_off_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t input_state_off_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t input_state_invert_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t input_state_invert_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t input_LOS_threshold_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t input_LOS_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t output_PRE_long_level_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_PRE_long_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t output_PRE_long_decay_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_PRE_long_decay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t output_PRE_short_level_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_PRE_short_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t output_PRE_short_decay_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_PRE_short_decay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
/* TODO - fix for vsc3312*/
static ssize_t input_terminate_low_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t input_terminate_low_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t input_terminate_high_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t input_terminate_high_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t output_level_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t output_mode_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t output_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t forward_OOB_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t forward_OOB_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t status_0_on_LOS_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t status_0_on_LOS_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t status_1_on_LOS_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t status_1_on_LOS_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t status_show (struct device *dev, struct device_attribute *attr, char *buf);
/* global */
static ssize_t global_connection_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t global_connection_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t global_ISE_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t global_ISE_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t global_input_state_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t global_input_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t global_input_LOS_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t global_input_LOS_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t global_output_PRE_long_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t global_output_PRE_long_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t global_output_PRE_short_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t global_output_PRE_short_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t global_output_level_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t global_output_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t global_output_state_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t global_output_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t global_status_0_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t global_status_0_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t global_status_1_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t global_status_1_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t core_config_word_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t core_config_word_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t core_left_bias_en_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t core_left_bias_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t core_right_bias_en_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t core_right_bias_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t core_buffer_on_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t core_buffer_on_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t core_config_pin_invert_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t core_config_pin_invert_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t soft_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t address_range_show (struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t address_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t status_combo_show (struct device *dev, struct device_attribute *attr, char *buf);
/* Global registers - writes applied to all port registers. No sense to read (so write only), but functions preserved
* Placed in "globals" directory */
static DEVICE_ATTR(connection, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, global_connection_show, global_connection_store);
static DEVICE_ATTR(ISE, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, global_ISE_show, global_ISE_store);
static DEVICE_ATTR(input_state, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, global_input_state_show, global_input_state_store);
static DEVICE_ATTR(input_LOS, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, global_input_LOS_show, global_input_LOS_store);
static DEVICE_ATTR(output_PRE_long, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, global_output_PRE_long_show, global_output_PRE_long_store);
static DEVICE_ATTR(output_PRE_short, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, global_output_PRE_short_show, global_output_PRE_short_store);
static DEVICE_ATTR(output_level, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, global_output_level_show, global_output_level_store);
static DEVICE_ATTR(output_state, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, global_output_state_show, global_output_state_store);
static DEVICE_ATTR(status_0_on_LOS, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, global_status_0_show, global_status_0_store);
static DEVICE_ATTR(status_1_on_LOS, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, global_status_1_show, global_status_1_store);
/* control/status registers , placed in "control" directory */
static DEVICE_ATTR(core_word, SYSFS_PERMISSIONS, core_config_word_show, core_config_word_store);
static DEVICE_ATTR(core_left_bias_en, SYSFS_PERMISSIONS, core_left_bias_en_show, core_left_bias_en_store);
static DEVICE_ATTR(core_right_bias_en,SYSFS_PERMISSIONS, core_right_bias_en_show, core_right_bias_en_store);
static DEVICE_ATTR(core_buffer_on, SYSFS_PERMISSIONS, core_buffer_on_show, core_buffer_on_store);
static DEVICE_ATTR(core_config_pin_invert,SYSFS_PERMISSIONS, core_config_pin_invert_show, core_config_pin_invert_store);
static DEVICE_ATTR(soft_reset, SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL, soft_reset_store);
static DEVICE_ATTR(address_range, SYSFS_PERMISSIONS, address_range_show, address_range_store);
static DEVICE_ATTR(status, SYSFS_PERMISSIONS & SYSFS_READONLY, status_combo_show, NULL);
static struct attribute *globals_dev_attrs[] = {
&dev_attr_connection.attr,
&dev_attr_ISE.attr,
&dev_attr_input_state.attr,
&dev_attr_input_LOS.attr,
&dev_attr_output_PRE_long.attr,
&dev_attr_output_PRE_short.attr,
&dev_attr_output_level.attr,
&dev_attr_output_state.attr,
&dev_attr_status_0_on_LOS.attr,
&dev_attr_status_1_on_LOS.attr,
NULL
};
static const struct attribute_group dev_attr_globals_group = {
.attrs = globals_dev_attrs,
.name = "globals",
};
static struct attribute *control_dev_attrs[] = {
&dev_attr_core_word.attr,
&dev_attr_core_left_bias_en.attr,
&dev_attr_core_right_bias_en.attr,
&dev_attr_core_buffer_on.attr,
&dev_attr_core_config_pin_invert.attr,
&dev_attr_soft_reset.attr,
&dev_attr_address_range.attr,
&dev_attr_status.attr,
NULL
};
static const struct attribute_group dev_attr_control_group = {
.attrs = control_dev_attrs,
.name = "control",
};
/* seems we need to INITIALIZE all structures to zero - examples use static */
static int make_group (struct device *dev, const char * name, int port_mask, mode_t mode,
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf),
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count))
{
int retval=-1;
int port,index=0,num_regs;
struct attribute **pattrs; /* array of pointers to attibutes */
struct device_attribute *dev_attrs;
struct attribute_group *attr_group;
for (port=0,num_regs=1;portname = name;
attr_group->attrs =pattrs;
dev_dbg(dev,"name=%s, &dev->kobj=0x%08x\n",attr_group->name, (int) (&dev->kobj));
index=0;
while ((*attr_group).attrs[index]){
dev_dbg(dev,"attr=%s\n",attr_group->attrs[index]->name);
index++;
}
if (&dev->kobj) {
retval = sysfs_create_group(&dev->kobj, attr_group);
}
return retval;
}
static int vsc330x_sysfs_register(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct vsc330x_data_t *clientdata = i2c_get_clientdata(client);
int ports=clientdata->out_ports;
int retval=0;
if (&dev->kobj) {
if ((retval=make_group (dev, "connections", ports, SYSFS_PERMISSIONS, connection_show, connection_store))) return retval;
if ((retval=make_group (dev, "input_ISE_short", ports, SYSFS_PERMISSIONS, input_ISE_short_show, input_ISE_short_store))) return retval;
if ((retval=make_group (dev, "input_ISE_medium", ports, SYSFS_PERMISSIONS, input_ISE_medium_show, input_ISE_medium_store))) return retval;
if ((retval=make_group (dev, "input_ISE_long", ports, SYSFS_PERMISSIONS, input_ISE_long_show, input_ISE_long_store))) return retval;
if ((retval=make_group (dev, "input_state_off", ports, SYSFS_PERMISSIONS, input_state_off_show, input_state_off_store))) return retval;
if ((retval=make_group (dev, "input_state_invert", ports, SYSFS_PERMISSIONS, input_state_invert_show, input_state_invert_store))) return retval;
if ((retval=make_group (dev, "input_LOS_threshold", ports, SYSFS_PERMISSIONS, input_LOS_threshold_show, input_LOS_threshold_store))) return retval;
if ((retval=make_group (dev, "output_PRE_long_level", ports, SYSFS_PERMISSIONS, output_PRE_long_level_show, output_PRE_long_level_store))) return retval;
if ((retval=make_group (dev, "output_PRE_long_decay", ports, SYSFS_PERMISSIONS, output_PRE_long_decay_show, output_PRE_long_decay_store))) return retval;
if ((retval=make_group (dev, "output_PRE_short_level",ports, SYSFS_PERMISSIONS, output_PRE_short_level_show, output_PRE_short_level_store))) return retval;
if ((retval=make_group (dev, "output_PRE_short_decay",ports, SYSFS_PERMISSIONS, output_PRE_short_decay_show, output_PRE_short_decay_store))) return retval;
if ((retval=make_group (dev, "input_terminate_low", ports, SYSFS_PERMISSIONS, input_terminate_low_show, input_terminate_low_store))) return retval;
if ((retval=make_group (dev, "input_terminate_high", ports, SYSFS_PERMISSIONS, input_terminate_high_show, input_terminate_high_store))) return retval;
if ((retval=make_group (dev, "output_level", ports, SYSFS_PERMISSIONS, output_level_show, output_level_store))) return retval;
if ((retval=make_group (dev, "output_mode", ports, SYSFS_PERMISSIONS, output_mode_show, output_mode_store))) return retval;
if ((retval=make_group (dev, "forward_OOB", ports, SYSFS_PERMISSIONS, forward_OOB_show, forward_OOB_store))) return retval;
if ((retval=make_group (dev, "status_0_on_LOS", ports, SYSFS_PERMISSIONS, status_0_on_LOS_show, status_0_on_LOS_store))) return retval;
if ((retval=make_group (dev, "status_1_on_LOS", ports, SYSFS_PERMISSIONS, status_1_on_LOS_show, status_1_on_LOS_store))) return retval;
if ((retval=make_group (dev, "status", ports, SYSFS_PERMISSIONS & SYSFS_READONLY, status_show, NULL))) return retval;
if ((retval = sysfs_create_group(&dev->kobj, &dev_attr_globals_group))) return retval;
if ((retval = sysfs_create_group(&dev->kobj, &dev_attr_control_group))) return retval;
}
return retval;
}
static ssize_t field_show(struct device *dev, struct device_attribute *attr, char *buf,
int page, int ls_bit_num, int width)
{
int reg, port_mask, rc=0, len=0, count=PAGE_SIZE;
struct i2c_client *client = to_i2c_client(dev);
/* do for all ports - not used. TODO: we can try to output all of them in a row, but not sure if count will permit*/
if (strcmp(attr->attr.name,ALL_PORTS) == 0) {
port_mask=((struct vsc330x_data_t *) i2c_get_clientdata(to_i2c_client(dev)))->out_ports;
for (reg=0;reg5)) {
dev_dbg(dev, "name='%s' reg=0x%x, page=0x%x, ls_bit_num=0x%x, width=0x%x\n",
attr->attr.name, reg, page, ls_bit_num, width);
rc = read_page_field(client, page, reg, ls_bit_num, width);
// rc=field_show_reg(dev, buf, page, reg, ls_bit_num, width);
if (rc<0) return rc;
rc=sprintf(buf, "%d ", rc);
buf+=rc;
len+=rc;
count-=rc;
}
if (len>0) {
len--;
count++;
buf--;
}
rc=sprintf(buf, "\n");
buf+=rc;
len+=rc;
count-=rc;
return len;
}
/* process single port */
sscanf(attr->attr.name+strlen(PORT_PEFIX), "%du", ®);
dev_dbg(dev, "name='%s' name+%d='%s' reg=0x%x, page=0x%x, ls_bit_num=0x%x, width=0x%x\n",
attr->attr.name, strlen(PORT_PEFIX), ( attr->attr.name+strlen(PORT_PEFIX)), reg, page, ls_bit_num, width);
return field_show_reg(dev, buf, page, reg, ls_bit_num, width);
}
static ssize_t field_show_reg(struct device *dev, char *buf, int page, int reg, int ls_bit_num, int width)
{
struct i2c_client *client = to_i2c_client(dev);
int data=read_page_field(client, page, reg, ls_bit_num, width);
return sprintf(buf, "%d\n", data);
}
static ssize_t field_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count,
int page, int ls_bit_num, int width)
{
int reg, port_mask, rc;
/* do for all ports */
if (strcmp(attr->attr.name,ALL_PORTS) == 0) {
port_mask=((struct vsc330x_data_t *) i2c_get_clientdata(to_i2c_client(dev)))->out_ports;
for (reg=0;regattr.name, reg, page, ls_bit_num, width);
rc= field_store_reg(dev, buf, count, page, reg, ls_bit_num, width);
if (rc<0) return rc;
}
return count;
}
/* process single port */
sscanf(attr->attr.name+strlen(PORT_PEFIX), "%du", ®);
dev_dbg(dev, "name='%s' name+%d='%s' reg=0x%x, page=0x%x, ls_bit_num=0x%x, width=0x%x\n",
attr->attr.name, strlen(PORT_PEFIX), ( attr->attr.name+strlen(PORT_PEFIX)), reg, page, ls_bit_num, width);
return field_store_reg(dev, buf, count, page, reg, ls_bit_num, width);
}
static ssize_t field_store_reg(struct device *dev, const char *buf, size_t count,
int page, int reg, int ls_bit_num, int width)
{
struct i2c_client *client = to_i2c_client(dev);
int val,rc;
sscanf(buf, "%du", &val);
rc=write_page_field(client, page, reg, val, ls_bit_num, width);
return count;
}
static ssize_t connection_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_CONNECTION, 0, 5);}
static ssize_t connection_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_CONNECTION, 0, 5);}
static ssize_t input_ISE_short_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_INPUT_ISE, 4, 2);}
static ssize_t input_ISE_short_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_INPUT_ISE, 4, 2);}
static ssize_t input_ISE_medium_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_INPUT_ISE, 2, 2);}
static ssize_t input_ISE_medium_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_INPUT_ISE, 2, 2);}
static ssize_t input_ISE_long_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_INPUT_ISE, 0, 2);}
static ssize_t input_ISE_long_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_INPUT_ISE, 0, 2);}
static ssize_t input_state_off_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_INPUT_STATE, 1, 1);}
static ssize_t input_state_off_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_INPUT_STATE, 1, 1);}
static ssize_t input_state_invert_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_INPUT_STATE, 0, 1);}
static ssize_t input_state_invert_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_INPUT_STATE, 0, 1);}
static ssize_t input_LOS_threshold_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_INPUT_LOS, 0, 3);}
static ssize_t input_LOS_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_INPUT_LOS, 0, 3);}
static ssize_t output_PRE_long_level_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_OUTPUT_PRE_LONG, 3, 4);}
static ssize_t output_PRE_long_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_OUTPUT_PRE_LONG, 3, 4);}
static ssize_t output_PRE_long_decay_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_OUTPUT_PRE_LONG, 0, 3);}
static ssize_t output_PRE_long_decay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_OUTPUT_PRE_LONG, 0, 3);}
static ssize_t output_PRE_short_level_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_OUTPUT_PRE_SHORT, 3, 4);}
static ssize_t output_PRE_short_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_OUTPUT_PRE_SHORT, 3, 4);}
static ssize_t output_PRE_short_decay_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_OUTPUT_PRE_SHORT, 0, 3);}
static ssize_t output_PRE_short_decay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_OUTPUT_PRE_SHORT, 0, 3);}
/* TODO - fix for vsc3312*/
static ssize_t input_terminate_low_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_INPUT_STATE, 2, 1);}
static ssize_t input_terminate_low_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_INPUT_STATE, 2, 1);}
static ssize_t input_terminate_high_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_OUTPUT_LEVEL, 4, 1);}
static ssize_t input_terminate_high_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_OUTPUT_LEVEL, 4, 1);}
static ssize_t output_level_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_OUTPUT_LEVEL, 0, 4);}
static ssize_t output_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_OUTPUT_LEVEL, 0, 4);}
static ssize_t output_mode_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_OUTPUT_STATE, 1, 4);}
static ssize_t output_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_OUTPUT_STATE, 1, 4);}
static ssize_t forward_OOB_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_OUTPUT_STATE, 0, 1);}
static ssize_t forward_OOB_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_OUTPUT_STATE, 0, 1);}
static ssize_t status_0_on_LOS_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_STATUS0_CONFIGURE, 0, 1);}
static ssize_t status_0_on_LOS_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_STATUS0_CONFIGURE, 0, 1);}
static ssize_t status_1_on_LOS_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_STATUS1_CONFIGURE, 0, 1);}
static ssize_t status_1_on_LOS_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store(dev, attr, buf, count, I2C_PAGE_STATUS1_CONFIGURE, 0, 1);}
/* per-port LOS status */
static ssize_t status_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show(dev, attr, buf, I2C_PAGE_STATUS_READ, 0, 1);}
/* combined LOS status */
static ssize_t status_combo_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg(dev, buf, I2C_PAGE_STATUS_READ, 0x10, 0, 2);}
/* Global registers use all 8 bits (and control multiple features at once, because it is not possible to read-modify-write them */
static ssize_t global_connection_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_GLOBAL_CONNECTION, 0, 8);}
static ssize_t global_connection_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_GLOBAL_CONNECTION, 0, 8);}
static ssize_t global_ISE_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_GLOBAL_INPUT_ISE, 0, 8);}
static ssize_t global_ISE_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_GLOBAL_INPUT_ISE, 0, 8);}
static ssize_t global_input_state_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_GLOBAL_INPUT_STATE, 0, 8);}
static ssize_t global_input_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_GLOBAL_INPUT_STATE, 0, 8);}
static ssize_t global_input_LOS_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_GLOBAL_INPUT_LOS, 0, 8);}
static ssize_t global_input_LOS_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_GLOBAL_INPUT_LOS, 0, 8);}
static ssize_t global_output_PRE_long_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_GLOBAL_OUTPUT_PRE_LONG, 0, 8);}
static ssize_t global_output_PRE_long_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_GLOBAL_OUTPUT_PRE_LONG, 0, 8);}
static ssize_t global_output_PRE_short_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_GLOBAL_OUTPUT_PRE_SHORT, 0, 8);}
static ssize_t global_output_PRE_short_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_GLOBAL_OUTPUT_PRE_SHORT, 0, 8);}
static ssize_t global_output_level_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_GLOBAL_OUTPUT_LEVEL, 0, 8);}
static ssize_t global_output_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_GLOBAL_OUTPUT_LEVEL, 0, 8);}
static ssize_t global_output_state_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_GLOBAL_OUTPUT_STATE, 0, 8);}
static ssize_t global_output_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_GLOBAL_OUTPUT_STATE, 0, 8);}
static ssize_t global_status_0_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_GLOBAL_STATUS0, 0, 8);}
static ssize_t global_status_0_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_GLOBAL_STATUS0, 0, 8);}
static ssize_t global_status_1_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_GLOBAL_STATUS1, 0, 8);}
static ssize_t global_status_1_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_GLOBAL_STATUS1, 0, 8);}
static ssize_t core_config_word_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 0, 8);}
static ssize_t core_config_word_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 0, 8);} /* 0x18/ 0x19 for 3312 */
static ssize_t core_left_bias_en_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 4, 1);}
static ssize_t core_left_bias_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 4, 1);}
static ssize_t core_right_bias_en_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 3, 1);}
static ssize_t core_right_bias_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 3, 1);}
static ssize_t core_buffer_on_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 1, 1);}
static ssize_t core_buffer_on_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 1, 1);}
static ssize_t core_config_pin_invert_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 0, 1);}
static ssize_t core_config_pin_invert_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 0, 1);}
static ssize_t soft_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
int rc;
if (((rc=field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_SOFTWARE_RESET, 4, 1)))<0) return rc;
if (((rc=init_device(client)))<0) return rc;
return count;
}
static ssize_t address_range_show (struct device *dev, struct device_attribute *attr, char *buf)
{return field_show_reg (dev, buf, I2C_PAGE_GLOBAL, I2C_SOFTWARE_RESET, 0, 4);}
static ssize_t address_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_SOFTWARE_RESET, 0, 4);}
/* Setup i2c, write address_mode. Needed after soft reset and power up */
static int _init_device(struct i2c_client *client, int address_mode_data)
{
int rc;
if (((rc=write_reg(client, I2C_INTERFACE_MODE, I2C_INTERFACE_MODE_DATA)))<0) return rc;
if (address_mode_data >=0){ /* only write if needed, 3304 needs, 3312 - does not, 3308 - ? */
// if (((rc=write_reg(client, I2C_SOFTWARE_RESET_DATA, address_mode_data)))<0) return rc;
// this bug caused me to assemble a new board, urgently find vsc3304 chip,spend several days trying to troubleshoot...
// Just five extra characters!
if (((rc=write_reg(client, I2C_SOFTWARE_RESET, address_mode_data)))<0) return rc;
}
if (((rc=write_reg(client, I2C_CURRENT_PAGE, 0)))<0) return rc;
return 0;
}
static int init_device(struct i2c_client *client)
{
int rc;
struct vsc330x_data_t *clientdata = i2c_get_clientdata(client);
dev_info(&client->dev,"Re-initializing %s\n",client->name);
if (((rc=_init_device(client, clientdata->address_mode_data)))<0) return rc;
clientdata->last_page=0;
return 0;
}
static int bitmask(int ls_bit_num, int width)
{
return ((1 << width) -1) << ls_bit_num;
}
//address_mode_data
static int read_reg(struct i2c_client *client, u8 reg)
{
int val;
val= i2c_smbus_read_byte_data(client, reg);
dev_dbg(&client->dev,"reading i2c device : slave=0x%x, reg=0x%x -> 0x%x\n",(int) (client->addr),reg,val);
return val;
}
static int write_reg(struct i2c_client *client, u8 reg, u8 val)
{
dev_dbg(&client->dev,"device write: slave=0x%x, reg=0x%x, val=0x%x\n", (int) (client->addr),reg,val);
return i2c_smbus_write_byte_data(client, reg, val);
}
static int read_field(struct i2c_client *client, u8 reg, int ls_bit_num, int width)
{
int rc;
if (((rc=read_reg(client, reg)))<0) return rc;
rc= (rc >> ls_bit_num) & bitmask(0, width);
dev_dbg(&client->dev,"reg=0x%x, ls_bit_num=%d, width=%d -> 0x%x\n", (int) reg, ls_bit_num,width,rc);
return rc;
}
static int read_page_field(struct i2c_client *client, int page, u8 reg, int ls_bit_num, int width)
{
int rc;
struct vsc330x_data_t *clientdata = i2c_get_clientdata(client);
dev_dbg(&client->dev,"page=0x%x (last was 0x%x) reg=0x%x, ls_bit_num=%d, width=%d:\n",
page, (clientdata->last_page!=page), (int) reg, ls_bit_num, width);
if ((page>=0) && (clientdata->last_page!=page)) {
if (((rc=write_reg(client, I2C_CURRENT_PAGE, page)))<0) return rc;
clientdata->last_page=page;
}
rc= read_field(client, reg, ls_bit_num, width);
dev_dbg(&client->dev,"page=0x%x (last was 0x%x) reg=0x%x, ls_bit_num=%d, width=%d -> 0x%x\n",
page, (clientdata->last_page!=page), (int) reg, ls_bit_num, width,rc);
return rc;
}
static int write_field(struct i2c_client *client, u8 reg, u8 val, int ls_bit_num, int width)
{
int rc;
dev_dbg(&client->dev,"reg=0x%x, val=0x%x, ls_bit_num=%d, width=%d \n", (int) reg, (int) val,ls_bit_num,width);
if (((rc=read_reg(client, reg)))<0) return rc;
val<<=ls_bit_num;
return write_reg(client, reg, ((rc ^ val) & bitmask(ls_bit_num, width))^ rc);
}
static int write_page_field(struct i2c_client *client, int page, u8 reg, u8 val, int ls_bit_num, int width)
{
int rc;
struct vsc330x_data_t *clientdata = i2c_get_clientdata(client);
dev_dbg(&client->dev,"page=0x%x (last was 0x%x), reg=0x%x, val=0x%x, ls_bit_num=%d, width=%d \n",
page,clientdata->last_page, (int) reg, (int) val,ls_bit_num,width);
if ((page>=0) && (clientdata->last_page!=page)) {
if (((rc=write_reg(client, I2C_CURRENT_PAGE, page)))<0) return rc;
clientdata->last_page=page;
}
return write_field(client, reg, val, ls_bit_num, width);
}
static int write_with_mask(struct i2c_client *client, u8 reg, u8 val, u8 mask)
{
int rc;
dev_dbg(&client->dev,"reg=0x%x, val=0x%x, mask=0x%x\n", (int) reg, (int) val, (int) mask);
if (mask !=0xff){
if (((rc=read_reg(client, reg)))<0) return rc;
val=((val ^ rc) & mask)^ rc;
}
return write_reg(client, reg, val);
}
static int write_page_with_mask(struct i2c_client *client, int page, u8 reg, u8 val , u8 mask)
{
int rc;
struct vsc330x_data_t *clientdata = i2c_get_clientdata(client);
dev_dbg(&client->dev,"page=0x%x (last was 0x%x), reg=0x%x, val=0x%x, mask=0x%x\n",
page,clientdata->last_page, (int) reg, (int) val,(int) mask);
if ((page>=0) && (clientdata->last_page!=page)) {
if (((rc=write_reg(client, I2C_CURRENT_PAGE, page)))<0) return rc;
clientdata->last_page=page;
}
return write_with_mask(client, reg, val, mask);
}
static void vsc330x_init_of(struct i2c_client *client)
{
// struct device *dev=&client->dev;
const __be32 * config_data;
struct device_node *node = client->dev.of_node;
int len,i,rc;
struct vsc330x_setup_data {
u8 page;
u8 reg;
u8 data;
u8 mask;
};
struct vsc330x_setup_data setup_data;
__be32 * setup_data_be32= (__be32 *) &setup_data;
const char * config_name;
if (node) {
config_name = of_get_property(client->dev.of_node, "vsc330x,configuration_name", &len);
if (config_name){
dev_info(&client->dev,"Initializing %s registers for \"%s\"\n",client->name,config_name);
}
config_data = of_get_property(client->dev.of_node, "vsc330x,configuration_data", &len);
if (config_data){
len /= sizeof(*config_data);
dev_dbg(&client->dev,"Read %d values\n",len);
for (i=0;idev,"0x%08x (0x%08x)\n", config_data[i],be32_to_cpup(config_data+i));
*setup_data_be32=config_data[i];
dev_dbg(&client->dev,"page=0x%02x, reg=0x%02x, data=0x%02x, mask=0x%02x \n",
(int)setup_data.page, (int)setup_data.reg,(int)setup_data.data,(int)setup_data.mask);
if (((rc=write_page_with_mask(client, (setup_data.page==0xff)?-1:setup_data.page, setup_data.reg,
setup_data.data, setup_data.mask)))<0) return;
}
} else {
dev_info(&client->dev,"'vsc330x,configuration_data' not found\n");
}
} else {
dev_info(&client->dev,"Device tree data not found for %s\n",client->name);
}
}
/*
dev_info(&client->dev,
*/
static int vsc330x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int rc=0;
struct vsc330x_data_t *clientdata = NULL;
/* initialize i2c mode and (if needed) address range bit field */
if (((rc=_init_device(client, vsc330x_data[id->driver_data].address_mode_data)))<0) goto wr_err;
dev_info(&client->dev,
"Chip %s found, driver version %s\n", id->name, DRV_VERSION);
clientdata = devm_kzalloc(&client->dev, sizeof(*clientdata), GFP_KERNEL);
if (!clientdata) {
rc = -ENOMEM;
goto exit;
}
clientdata->last_page = 0;
clientdata->address_mode_data = vsc330x_data[id->driver_data].address_mode_data;
clientdata->in_ports = vsc330x_data[id->driver_data].in_ports;
clientdata->out_ports = vsc330x_data[id->driver_data].out_ports;
i2c_set_clientdata(client, clientdata);
rc = vsc330x_sysfs_register(&client->dev);
if (rc)
goto exit;
vsc330x_init_of(client);
return 0; /* found OK*/
wr_err:
rc = -EIO;
dev_err(&client->dev, "%s:%d error writing\n",__func__,__LINE__);
goto exit;
#if 0
rd_err:
rc = -EIO;
dev_err(&client->dev, "%s:%d error reading\n",__func__,__LINE__);
goto exit;
#endif
exit:
return rc;
}
static int vsc330x_i2c_remove(struct i2c_client *client)
{
return 0;
}
static struct i2c_driver vsc330x_i2c_driver = {
.driver = {
.name = "vsc330x",
.owner = THIS_MODULE,
},
.probe = vsc330x_i2c_probe,
.remove = vsc330x_i2c_remove,
.id_table = vsc330x_id,
};
module_i2c_driver(vsc330x_i2c_driver);
MODULE_AUTHOR("Andrey Filippov ");
MODULE_DESCRIPTION("VSC330x I2C bus driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("i2c:vsc330x");
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/helpers/ 0000775 0000000 0000000 00000000000 12666203034 0023430 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/helpers/si5338_register_map_dts.py 0000775 0000000 0000000 00000006403 12666203034 0030361 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
#***************************************************************************
# FILE NAME : si5338_register_map_dts.py
# DESCRIPTION: convert si5338 register map file generated by Silicon Labs
# ClockBuilder(tm) Desktop Software into a device tree fragment, compatible
# with drivers/misc/si5338.c
# AUTHOR: Oleg Dzhimiev
# Copyright (C) 2013 Elphel, 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 3 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.
#
# The four essential freedoms with GNU GPL software:
# * to run the program for any purpose
# * to study how the program works and change it to make it do what you wish
# * to redistribute copies so you can help your neighbor
# * to distribute copies of your modified versions to others
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
# -----------------------------------------------------------------------------**
__author__ = "Oleg Dzhimiev"
__copyright__ = "Copyright 2013, Elphel, Inc."
__license__ = "GPL"
__version__ = "3.0+"
__maintainer__ = "Oleg Dzhimiev"
__email__ = "oleg@elphel.com"
__status__ = "Development"
import sys
try:
File = sys.argv[1]
except IndexError:
print '''
Usage - terminal:
a) ./register_map_dts.py
b) python register_map_dts.py
where is a *.h generated by Si5338 ClockBuilder
Example:
./register_map_dts.py register_map.h
Output:
.dts
'''
sys.exit()
dts_record_start = '''ps7_axi_interconnect_0: amba@0 {
ps7_i2c_0: ps7-i2c@e0004000 {
si5338@70 {
compatible = "sil,si5338";
reg = <0x70>;
si5338,init="always"; /* initialize PLL, wait for lock. Other option is 'if off'*/
si5338,configuration_data=<
'''
dts_record_end = '''
>;
};
};
};
'''
class RegisterMapParser:
def __init__(self,File):
self.File = File
def generate_dts(self):
print "Parsing "+self.File+".\n"
i,page,tmpstr = 0,0,""
with open(self.File,'r') as f_r:
for line in f_r:
if line[0]=='{':
# i+=1
line = line[line.find('{')+1:line.find('}')]
values = line.split(',')
if values[0]=='255':
page = int(values[1],16)
else:
if int(values[2],16) != 0 :
result = hex(((256*page+int(values[0]))<<16)+(int(values[1],16)<<8)+int(values[2],16))
tmpstr += result+" "
i+=1
if i%8==0 : tmpstr += "\n\t\t\t"
return tmpstr
Parser = RegisterMapParser(File)
with open(File+".dts",'w') as f_w:
f_w.write(dts_record_start+Parser.generate_dts()+dts_record_end)
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/include/ 0000775 0000000 0000000 00000000000 12666203034 0023411 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/include/elphel/ 0000775 0000000 0000000 00000000000 12666203034 0024662 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/include/elphel/elphel393-mem.h 0000664 0000000 0000000 00000002700 12666203034 0027316 0 ustar 00root root 0000000 0000000 /*!***************************************************************************
*! FILE NAME : elphel393-mem.h
*! DESCRIPTION:
*! Copyright (C) 2015 Elphel, 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 3 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 .
*!****************************************************************************/
struct elphel_buf_t
{
// Coherent DMA buffer
void *vaddr;
dma_addr_t paddr;
ssize_t size;
// Host to device stream DMA buffer
void *h2d_vaddr;
dma_addr_t h2d_paddr;
ssize_t h2d_size;
// Device to host stream DMA buffer
void *d2h_vaddr;
dma_addr_t d2h_paddr;
ssize_t d2h_size;
// Bidirectional stream DMA buffer
void *bidir_vaddr;
dma_addr_t bidir_paddr;
ssize_t bidir_size;
};
extern struct elphel_buf_t *pElphel_buf;
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/include/linux/ 0000775 0000000 0000000 00000000000 12666203034 0024550 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/include/linux/i2c/ 0000775 0000000 0000000 00000000000 12666203034 0025225 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/include/linux/i2c/ltc3589.h 0000664 0000000 0000000 00000013275 12666203034 0026521 0 ustar 00root root 0000000 0000000 /*!***************************************************************************
*! FILE NAME : ltc3589.c
*! DESCRIPTION: control of the Linear Technology LTC3589 8-channel voltage regulator
*! Copyright (C) 2013 Elphel, 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 3 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 .
*/
#ifndef __LINUX_LTC3589_H
#define __LINUX_LTC3589_H
#include
#include
#include
int ltc3589_read_field(struct i2c_client *client, u32 awe);
int ltc3589_write_field(struct i2c_client *client, u8 data, u32 awe);
int ltc3589_write_adwe(struct i2c_client *client, u32 adwe);
void ltc3589_set_simulate(struct i2c_client *client, int simulate);
#define LTC3589_AWE_SCR1 0x07ff
#define LTC3589_AWE_SCR1_MODE_SD1 0x0703
#define LTC3589_AWE_SCR1_MODE_SD2 0x070c
#define LTC3589_AWE_SCR1_MODE_SD3 0x0730
#define LTC3589_AWE_SCR1_MODE_BB 0x0740
#define LTC3589_AWE_OVEN 0x10ff
#define LTC3589_AWE_OVEN_EN_SD1 0x1001
#define LTC3589_AWE_OVEN_EN_SD2 0x1002
#define LTC3589_AWE_OVEN_EN_SD3 0x1004
#define LTC3589_AWE_OVEN_EN_BB 0x1008
#define LTC3589_AWE_OVEN_EN_LDO2 0x1010
#define LTC3589_AWE_OVEN_EN_LDO3 0x1020
#define LTC3589_AWE_OVEN_EN_LDO4 0x1040
#define LTC3589_AWE_OVEN_ONLY 0x1080
#define LTC3589_AWE_SCR2 0x12ff
#define LTC3589_AWE_SCR2_NOWAIT_SD1 0x1201
#define LTC3589_AWE_SCR2_NOWAIT_SD2 0x1202
#define LTC3589_AWE_SCR2_NOWAIT_SD3 0x1204
#define LTC3589_AWE_SCR2_NOWAIT_BB 0x1208
#define LTC3589_AWE_SCR2_NOWAIT_LDO2 0x1210
#define LTC3589_AWE_SCR2_NOWAIT_LDO3 0x1220
#define LTC3589_AWE_SCR2_NOWAIT_LDO4 0x1240
#define LTC3589_AWE_SCR2_PGOOD_SHTDN_INH 0x1280
#define LTC3589_AWE_VCCR 0x20ff
#define LTC3589_AWE_VCCR_SLEW_SD1 0x2001 /* self clearing bit */
#define LTC3589_AWE_VCCR_REF_SEL_SD1 0x2002
#define LTC3589_AWE_VCCR_SLEW_SD2 0x2004 /* self clearing bit */
#define LTC3589_AWE_VCCR_REF_SEL_SD2 0x2008
#define LTC3589_AWE_VCCR_SLEW_SD3 0x2010 /* self clearing bit */
#define LTC3589_AWE_VCCR_REF_SEL_SD3 0x2020
#define LTC3589_AWE_VCCR_SLEW_LDO2 0x2040 /* self clearing bit */
#define LTC3589_AWE_VCCR_REF_SEL_LDO2 0x2080
#define LTC3589_AWE_CLIRQ 0x21ff
#define LTC3589_AWE_B1DTV1 0x23ff
#define LTC3589_AWE_B1DTV1_REF 0x231f
#define LTC3589_AWE_B1DTV1_PGMASK 0x2320
#define LTC3589_AWE_B1DTV1_DVDT 0x23c0
#define LTC3589_AWE_B1DTV2 0x24ff
#define LTC3589_AWE_B1DTV2_REF 0x241f
#define LTC3589_AWE_B1DTV2_CLKRATE 0x2420
#define LTC3589_AWE_B1DTV2_PHASE 0x2440
#define LTC3589_AWE_B1DTV2_KEEP_ALIVE 0x2480
#define LTC3589_AWE_VRRCR 0x25ff
#define LTC3589_AWE_VRRCR_SD1 0x2503
#define LTC3589_AWE_VRRCR_SD2 0x250c
#define LTC3589_AWE_VRRCR_SD3 0x2530
#define LTC3589_AWE_VRRCR_LDO2 0x25c0
#define LTC3589_AWE_B2DTV1 0x26ff
#define LTC3589_AWE_B2DTV1_REF 0x261f
#define LTC3589_AWE_B2DTV1_PGMASK 0x2620
#define LTC3589_AWE_B2DTV2 0x27ff
#define LTC3589_AWE_B2DTV2_REF 0x271f
#define LTC3589_AWE_B2DTV2_CLKRATE 0x2720
#define LTC3589_AWE_B2DTV2_PHASE 0x2740
#define LTC3589_AWE_B2DTV2_KEEP_ALIVE 0x2780
#define LTC3589_AWE_B3DTV1 0x29ff
#define LTC3589_AWE_B3DTV1_REF 0x291f
#define LTC3589_AWE_B3DTV1_PGMASK 0x2920
#define LTC3589_AWE_B3DTV2 0x2aff
#define LTC3589_AWE_B3DTV2_REF 0x2a1f
#define LTC3589_AWE_B3DTV2_CLKRATE 0x2a20
#define LTC3589_AWE_B3DTV2_PHASE 0x2a40
#define LTC3589_AWE_B3DTV2_KEEP_ALIVE 0x2a80
#define LTC3589_AWE_L2DTV1 0x32ff
#define LTC3589_AWE_L2DTV1_REF 0x321f
#define LTC3589_AWE_L2DTV1_PGMASK 0x3220
#define LTC3589_AWE_L2DTV1_KEEP_ALIVE 0x3280
#define LTC3589_AWE_L2DTV2 0x33ff
#define LTC3589_AWE_L2DTV2_REF 0x331f
#define LTC3589_AWE_L2DTV2_REF_LDO4 0x3360
#define LTC3589_AWE_L2DTV2_MODE_LDO4 0x3380
#define LTC3589_AWE_IRQSTAT 0x02ff
#define LTC3589_AWE_IRQSTAT_PGOOD_TIMOUT 0x0208
#define LTC3589_AWE_IRQSTAT_NEAR_UV 0x0210
#define LTC3589_AWE_IRQSTAT_HARD_UV 0x0220
#define LTC3589_AWE_IRQSTAT_NEAR_THERM 0x0240
#define LTC3589_AWE_IRQSTAT_HARD_THERM 0x0280
#define LTC3589_AWE_PGSTAT 0x13ff
#define LTC3589_AWE_PGSTAT_LDO1 0x1301
#define LTC3589_AWE_PGSTAT_SD1 0x1302
#define LTC3589_AWE_PGSTAT_SD2 0x1304
#define LTC3589_AWE_PGSTAT_SD3 0x1308
#define LTC3589_AWE_PGSTAT_BB 0x1310
#define LTC3589_AWE_PGSTAT_LDO2 0x1320
#define LTC3589_AWE_PGSTAT_LDO3 0x1340
#define LTC3589_AWE_PGSTAT_LDO4 0x1380
#endif /* __LINUX_LTC3589_H */
linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/other/ 0000775 0000000 0000000 00000000000 12666203034 0023107 5 ustar 00root root 0000000 0000000 linux-elphel-baaa78051cb4def07c607e606cb78a19508f671b/src/other/mem.py 0000664 0000000 0000000 00000003551 12666203034 0024243 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2013, Elphel.inc.
# configuration of the DDR-related registers
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 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 .
__author__ = "Andrey Filippov"
__copyright__ = "Copyright 2014, Elphel, Inc."
__license__ = "GPL"
__version__ = "3.0+"
__maintainer__ = "Andrey Filippov"
__email__ = "andrey@elphel.com"
__status__ = "Development"
import mmap
import sys
import struct
PAGE_SIZE=4096
endian="<" # little, ">" for big
if len(sys.argv)<1:
print "Usage: ", sys.argv[0]+" address [data]"
exit (0)
addr=int(sys.argv[1],16) & 0xfffffffc
data=0
writeMode=len(sys.argv)>2
if (writeMode):
data=int(sys.argv[2],16)
with open("/dev/mem", "r+b") as f:
page_addr=addr & (~(PAGE_SIZE-1))
page_offs=addr-page_addr
#in python 2.7.11 works w/o negation
#if (page_addr>=0x80000000):
# page_addr-= (1<<32)
mm = mmap.mmap(f.fileno(), PAGE_SIZE, offset=page_addr)
if writeMode:
packedData=struct.pack(endian+"L",data)
d=struct.unpack(endian+"L",packedData)[0]
mm[page_offs:page_offs+4]=packedData
print ("0x%08x <== 0x%08x (%d)"%(addr,d,d))
else:
data=struct.unpack(endian+"L",mm[page_offs:page_offs+4])
d=data[0]
print ("0x%08x ==> 0x%08x (%d)"%(addr,d,d))
mm.close()