build: upgrade to go 1.17 and dependencies
This commit is contained in:
433
vendor/periph.io/x/host/v3/.gohci.yml
generated
vendored
Normal file
433
vendor/periph.io/x/host/v3/.gohci.yml
generated
vendored
Normal file
@ -0,0 +1,433 @@
|
||||
# Copyright 2021 The Periph Authors. All rights reserved.
|
||||
# Use of this source code is governed under the Apache License, Version 2.0
|
||||
# that can be found in the LICENSE file.
|
||||
|
||||
# See https://github.com/periph/gohci
|
||||
version: 1
|
||||
workers:
|
||||
# BeagleBone Green Wireles by SeedStudio.
|
||||
# https://beagleboard.org/green-wireless
|
||||
- name: beaglebone-1860
|
||||
checks:
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- -cover
|
||||
- -bench=.
|
||||
- -benchtime=1000ms
|
||||
- -benchmem
|
||||
- ./...
|
||||
# Test in advance.
|
||||
- cmd:
|
||||
- git
|
||||
- clone
|
||||
- --depth
|
||||
- 1
|
||||
- https://github.com/periph/devices
|
||||
dir: ..
|
||||
- cmd:
|
||||
- go
|
||||
- get
|
||||
- periph.io/x/host/v3@${GIT_SHA}
|
||||
dir: ../devices
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- ./...
|
||||
dir: ../devices
|
||||
# Test in advance.
|
||||
- cmd:
|
||||
- git
|
||||
- clone
|
||||
- --depth
|
||||
- 1
|
||||
- https://github.com/periph/cmd
|
||||
dir: ..
|
||||
- cmd:
|
||||
- go
|
||||
- get
|
||||
- periph.io/x/host/v3@${GIT_SHA}
|
||||
dir: ../cmd
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- ./...
|
||||
dir: ../cmd
|
||||
# Test commands.
|
||||
- cmd:
|
||||
- go
|
||||
- install
|
||||
- -v
|
||||
- ./headers-list
|
||||
- ./i2c-list
|
||||
- ./periph-info
|
||||
- ./periph-smoketest
|
||||
- ./spi-list
|
||||
dir: ../cmd
|
||||
- cmd:
|
||||
- periph-info
|
||||
- cmd:
|
||||
- headers-list
|
||||
- -f
|
||||
- cmd:
|
||||
- i2c-list
|
||||
- cmd:
|
||||
- spi-list
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- gpio
|
||||
- -pin1
|
||||
- P8_45
|
||||
- -pin2
|
||||
- P8_46
|
||||
|
||||
# ODROID-C1+ by HardKernel
|
||||
# https://www.hardkernel.com/shop/odroid-c1/
|
||||
- name: odroid-483d
|
||||
checks:
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- -cover
|
||||
- -bench=.
|
||||
- -benchtime=1000ms
|
||||
- -benchmem
|
||||
- ./...
|
||||
# Test in advance.
|
||||
- cmd:
|
||||
- git
|
||||
- clone
|
||||
- --depth
|
||||
- 1
|
||||
- https://github.com/periph/devices
|
||||
dir: ..
|
||||
- cmd:
|
||||
- go
|
||||
- get
|
||||
- periph.io/x/host/v3@${GIT_SHA}
|
||||
dir: ../devices
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- ./...
|
||||
dir: ../devices
|
||||
# Test in advance.
|
||||
- cmd:
|
||||
- git
|
||||
- clone
|
||||
- --depth
|
||||
- 1
|
||||
- https://github.com/periph/cmd
|
||||
dir: ..
|
||||
- cmd:
|
||||
- go
|
||||
- get
|
||||
- periph.io/x/host/v3@${GIT_SHA}
|
||||
dir: ../cmd
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- ./...
|
||||
dir: ../cmd
|
||||
# Test commands.
|
||||
- cmd:
|
||||
- go
|
||||
- install
|
||||
- -v
|
||||
- ./gpio-list
|
||||
- ./headers-list
|
||||
- ./i2c-list
|
||||
- ./periph-info
|
||||
- ./periph-smoketest
|
||||
- ./spi-list
|
||||
dir: ../cmd
|
||||
- cmd:
|
||||
- periph-info
|
||||
- cmd:
|
||||
- gpio-list
|
||||
- -f
|
||||
- cmd:
|
||||
- headers-list
|
||||
- -f
|
||||
- cmd:
|
||||
- i2c-list
|
||||
- cmd:
|
||||
- spi-list
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- odroid-c1
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- i2c-testboard
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- onewire-testboard
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- spi-testboard
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- sysfs-benchmark
|
||||
- -p
|
||||
- 97
|
||||
- -short
|
||||
|
||||
# Raspberry Pi 3
|
||||
- name: raspberrypi-2f34
|
||||
checks:
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- -cover
|
||||
- -bench=.
|
||||
- -benchtime=1000ms
|
||||
- -benchmem
|
||||
- ./...
|
||||
# Test in advance.
|
||||
- cmd:
|
||||
- git
|
||||
- clone
|
||||
- --depth
|
||||
- 1
|
||||
- https://github.com/periph/devices
|
||||
dir: ..
|
||||
- cmd:
|
||||
- go
|
||||
- get
|
||||
- periph.io/x/host/v3@${GIT_SHA}
|
||||
dir: ../devices
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- ./...
|
||||
dir: ../devices
|
||||
# Test in advance.
|
||||
- cmd:
|
||||
- git
|
||||
- clone
|
||||
- --depth
|
||||
- 1
|
||||
- https://github.com/periph/cmd
|
||||
dir: ..
|
||||
- cmd:
|
||||
- go
|
||||
- get
|
||||
- periph.io/x/host/v3@${GIT_SHA}
|
||||
dir: ../cmd
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- ./...
|
||||
dir: ../cmd
|
||||
# Test commands.
|
||||
- cmd:
|
||||
- go
|
||||
- install
|
||||
- -v
|
||||
- ./gpio-list
|
||||
- ./headers-list
|
||||
- ./i2c-list
|
||||
- ./periph-info
|
||||
- ./periph-smoketest
|
||||
- ./spi-list
|
||||
dir: ../cmd
|
||||
- cmd:
|
||||
- periph-info
|
||||
- cmd:
|
||||
- gpio-list
|
||||
- -f
|
||||
- cmd:
|
||||
- headers-list
|
||||
- -f
|
||||
- cmd:
|
||||
- i2c-list
|
||||
- cmd:
|
||||
- spi-list
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- i2c-testboard
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- onewire-testboard
|
||||
- -i2cbus
|
||||
- 1
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- spi-testboard
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- sysfs-benchmark
|
||||
- -p
|
||||
- 12
|
||||
- -short
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- bcm283x-benchmark
|
||||
- -p
|
||||
- 12
|
||||
- -short
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- gpio
|
||||
- -pin1
|
||||
- P1_15
|
||||
- -pin2
|
||||
- P1_16
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- bcm283x
|
||||
- -quick
|
||||
|
||||
# Old MacBook Pro on 10.9.
|
||||
- name: mbp
|
||||
checks:
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- -cover
|
||||
- -bench=.
|
||||
- -benchtime=1000ms
|
||||
- -benchmem
|
||||
- ./...
|
||||
# Test in advance.
|
||||
- cmd:
|
||||
- git
|
||||
- clone
|
||||
- --depth
|
||||
- 1
|
||||
- https://github.com/periph/devices
|
||||
dir: ..
|
||||
- cmd:
|
||||
- go
|
||||
- get
|
||||
- periph.io/x/host/v3@${GIT_SHA}
|
||||
dir: ../devices
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- ./...
|
||||
dir: ../devices
|
||||
# Test in advance.
|
||||
- cmd:
|
||||
- git
|
||||
- clone
|
||||
- --depth
|
||||
- 1
|
||||
- https://github.com/periph/cmd
|
||||
dir: ..
|
||||
- cmd:
|
||||
- go
|
||||
- get
|
||||
- periph.io/x/host/v3@${GIT_SHA}
|
||||
dir: ../cmd
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- ./...
|
||||
dir: ../cmd
|
||||
# Test commands.
|
||||
- cmd:
|
||||
- go
|
||||
- install
|
||||
- -v
|
||||
- ./gpio-list
|
||||
- ./headers-list
|
||||
- ./i2c-list
|
||||
- ./periph-info
|
||||
- ./periph-smoketest
|
||||
- ./spi-list
|
||||
dir: ../cmd
|
||||
- cmd:
|
||||
- periph-info
|
||||
- cmd:
|
||||
- gpio-list
|
||||
- -f
|
||||
- cmd:
|
||||
- headers-list
|
||||
- -f
|
||||
- cmd:
|
||||
- i2c-list
|
||||
- cmd:
|
||||
- spi-list
|
||||
# - cmd:
|
||||
# - periph-smoketest
|
||||
# - ftdi
|
||||
# - -type
|
||||
# - ft232r
|
||||
|
||||
# Laptop on Windows 10.
|
||||
- name: win10
|
||||
checks:
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- -cover
|
||||
- -bench=.
|
||||
- -benchtime=1000ms
|
||||
- -benchmem
|
||||
- ./...
|
||||
# Test in advance.
|
||||
- cmd:
|
||||
- git
|
||||
- clone
|
||||
- --depth
|
||||
- 1
|
||||
- https://github.com/periph/devices
|
||||
dir: ..
|
||||
- cmd:
|
||||
- go
|
||||
- get
|
||||
- periph.io/x/host/v3@${GIT_SHA}
|
||||
dir: ../devices
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- ./...
|
||||
dir: ../devices
|
||||
# Test in advance.
|
||||
- cmd:
|
||||
- git
|
||||
- clone
|
||||
- --depth
|
||||
- 1
|
||||
- https://github.com/periph/cmd
|
||||
dir: ..
|
||||
- cmd:
|
||||
- go
|
||||
- get
|
||||
- periph.io/x/host/v3@${GIT_SHA}
|
||||
dir: ../cmd
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- ./...
|
||||
dir: ../cmd
|
||||
# Test commands.
|
||||
- cmd:
|
||||
- go
|
||||
- install
|
||||
- -v
|
||||
- ./gpio-list
|
||||
- ./headers-list
|
||||
- ./i2c-list
|
||||
- ./periph-info
|
||||
- ./periph-smoketest
|
||||
- ./spi-list
|
||||
dir: ../cmd
|
||||
- cmd:
|
||||
- periph-info
|
||||
- cmd:
|
||||
- gpio-list
|
||||
- -f
|
||||
- cmd:
|
||||
- headers-list
|
||||
- -f
|
||||
- cmd:
|
||||
- i2c-list
|
||||
- cmd:
|
||||
- spi-list
|
||||
# - cmd:
|
||||
# - periph-smoketest
|
||||
# - ftdi
|
||||
# - -type
|
||||
# - ft232h
|
15
vendor/periph.io/x/host/v3/AUTHORS
generated
vendored
Normal file
15
vendor/periph.io/x/host/v3/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# This is the list of The Periph Authors for copyright purposes.
|
||||
#
|
||||
# This does not necessarily list everyone who has contributed code, since in
|
||||
# some cases, their employer may be the copyright holder. To see the full list
|
||||
# of contributors, see the revision history in source control.
|
||||
Cássio Botaro <cassiobotaro@gmail.com>
|
||||
Fractal Industries, Inc
|
||||
Google Inc.
|
||||
Josh Gardiner
|
||||
Matt Aimonetti <mattaimonetti@gmail.com>
|
||||
Max Ekman <max@looplab.se>
|
||||
Rifiniti, Inc
|
||||
Stephan Sperber
|
||||
Thorsten von Eicken <tve@voneicken.com>
|
||||
|
4
vendor/periph.io/x/host/v3/CONTRIBUTING.md
generated
vendored
Normal file
4
vendor/periph.io/x/host/v3/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Contributing
|
||||
|
||||
Thanks for contributing to the project! Please look at [the periph contribution
|
||||
guidelines](https://periph.io/project/contributing/) first.
|
41
vendor/periph.io/x/host/v3/CONTRIBUTORS
generated
vendored
Normal file
41
vendor/periph.io/x/host/v3/CONTRIBUTORS
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# This is the official list of people who can contribute
|
||||
# (and typically have contributed) code to the periph repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# https://cla.developers.google.com/
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
|
||||
# Names should be added to this file like so:
|
||||
# Individual's name <submission email address>
|
||||
# Individual's name <submission email address> <email2> <emailN>
|
||||
#
|
||||
# An entry with multiple email addresses specifies that the
|
||||
# first address should be used in the submit logs and
|
||||
# that the other addresses should be recognized as the
|
||||
# same person when interacting with Gerrit.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Cássio Botaro <cassiobotaro@gmail.com>
|
||||
Eugene Dzhurynsky <jdevelop@gmail.com>
|
||||
Hidetoshi Shimokawa <smkwhdts@gmail.com>
|
||||
John Maguire <john.maguire@gmail.com>
|
||||
Josh Gardiner <josh@zool.com>
|
||||
Marc-Antoine Ruel <maruel@chromium.org> <maruel@gmail.com>
|
||||
Matt Aimonetti <mattaimonetti@gmail.com>
|
||||
Max Ekman <max@looplab.se>
|
||||
Matias Insaurralde <matias@insaurral.de>
|
||||
Seán C McCord <ulexus@gmail.com> <scm@cycoresys.com>
|
||||
Stephan Sperber <sperberstephan@googlemail.com>
|
||||
Thorsten von Eicken <tve@voneicken.com>
|
||||
|
202
vendor/periph.io/x/host/v3/LICENSE
generated
vendored
Normal file
202
vendor/periph.io/x/host/v3/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
57
vendor/periph.io/x/host/v3/README.md
generated
vendored
Normal file
57
vendor/periph.io/x/host/v3/README.md
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
# periph - Peripherals I/O in Go
|
||||
|
||||
Documentation is at https://periph.io
|
||||
|
||||
Join us for a chat on
|
||||
[gophers.slack.com/messages/periph](https://gophers.slack.com/messages/periph),
|
||||
get an [invite here](https://invite.slack.golangbridge.org/).
|
||||
|
||||
[](https://periph.io/)
|
||||
|
||||
[](https://pkg.go.dev/periph.io/x/host/v3)
|
||||
[](https://codecov.io/gh/periph/host)
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
Blink a LED:
|
||||
|
||||
~~~go
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/host/v3"
|
||||
"periph.io/x/host/v3/rpi"
|
||||
)
|
||||
|
||||
func main() {
|
||||
host.Init()
|
||||
t := time.NewTicker(500 * time.Millisecond)
|
||||
for l := gpio.Low; ; l = !l {
|
||||
rpi.P1_33.Out(l)
|
||||
<-t.C
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
Curious? Look at [supported devices](https://periph.io/device/) for more
|
||||
examples!
|
||||
|
||||
|
||||
## Authors
|
||||
|
||||
`periph` was initiated with ❤️️ and passion by [Marc-Antoine
|
||||
Ruel](https://github.com/maruel). The full list of contributors is in
|
||||
[AUTHORS](https://github.com/periph/host/blob/main/AUTHORS) and
|
||||
[CONTRIBUTORS](https://github.com/periph/host/blob/main/CONTRIBUTORS).
|
||||
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This is not an official Google product (experimental or otherwise), it
|
||||
is just code that happens to be owned by Google.
|
||||
|
||||
This project is not affiliated with the Go project.
|
220
vendor/periph.io/x/host/v3/allwinner/a20.go
generated
vendored
Normal file
220
vendor/periph.io/x/host/v3/allwinner/a20.go
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// This file contains pin mapping information that is specific to the Allwinner
|
||||
// A20 model.
|
||||
|
||||
package allwinner
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"periph.io/x/conn/v3/pin"
|
||||
"periph.io/x/host/v3/sysfs"
|
||||
)
|
||||
|
||||
// mappingA20 describes the mapping of the A20 processor gpios to their
|
||||
// alternate functions.
|
||||
//
|
||||
// It omits the in & out functions which are available on all gpio.
|
||||
//
|
||||
// The mapping comes from the datasheet page 241:
|
||||
// http://dl.linux-sunxi.org/A20/A20%20User%20Manual%202013-03-22.pdf
|
||||
var mappingA20 = map[string][5]pin.Func{
|
||||
"PA0": {"ERXD3", "SPI1_CS0", "UART2_RTS", "GRXD3"},
|
||||
"PA1": {"ERXD2", "SPI1_CLK", "UART2_CTS", "GRXD2"},
|
||||
"PA2": {"ERXD1", "SPI1_MOSI", "UART2_TX", "GRXD1"},
|
||||
"PA3": {"ERXD0", "SPI1_MISO", "UART2_RX", "GRXD0"},
|
||||
"PA4": {"ETXD3", "SPI1_CS1", "", "GTXD3"},
|
||||
"PA5": {"ETXD2", "SPI3_CS0", "", "GTXD2"},
|
||||
"PA6": {"ETXD1", "SPI3_CLK", "", "GTXD1"},
|
||||
"PA7": {"ETXD0", "SPI3_MOSI", "", "GTXD0"},
|
||||
"PA8": {"ERXCK", "SPI3_MISO", "", "CRXCK"},
|
||||
"PA9": {"ERXERR", "SPI3_CS1", "", "GNULL", "I2S1_MCLK"},
|
||||
"PA10": {"ERXDV", "", "UART1_TX", "GRXCTL"},
|
||||
"PA11": {"EMDC", "", "UART1_RX", "GMDC"},
|
||||
"PA12": {"EMDIO", "UART6_TX", "UART1_RTS", "GMDIO"},
|
||||
"PA13": {"ETXEN", "UART6_RX", "UART1_CTS", "GTXCTL"},
|
||||
"PA14": {"ETXCK", "UART7_TX", "UART1_DTR", "GNULL", "I2S1_SCK"},
|
||||
"PA15": {"ECRS", "UART7_RX", "UART1_DSR", "GTXCK", "I2S1_WS"},
|
||||
"PA16": {"ECOL", "CAN_TX", "UART1_DCD", "GCLKIN", "I2S1_DOUT"},
|
||||
"PA17": {"ETXERR", "CAN_RX", "UART1_RI", "GNULL", "I2S1_DIN"},
|
||||
"PB0": {"I2C0_SCL"},
|
||||
"PB1": {"I2C0_SDA"},
|
||||
"PB2": {"PWM0"},
|
||||
"PB3": {"IR0_TX", "", "SPDIF_MCLK", "", "STANBYWFI"},
|
||||
"PB4": {"IR0_RX"},
|
||||
"PB5": {"I2S0_MCLK", "AC97_MCLK"},
|
||||
"PB6": {"I2S0_SCK", "AC97_SCK"},
|
||||
"PB7": {"I2S0_WS", "AC97_SYNC"},
|
||||
"PB8": {"I2S0_DOUT0", "AC97_DOUT"},
|
||||
"PB9": {"I2S0_DOUT1"},
|
||||
"PB10": {"I2S0_DOUT2"},
|
||||
"PB11": {"I2S0_DOUT3"},
|
||||
"PB12": {"I2S0_DIN", "AC97_DI", "SPDIF_DI"},
|
||||
"PB13": {"SPI2_CS1", "", "SPDIF_DO"},
|
||||
"PB14": {"SPI2_CS0", "JTAG0_TMS"},
|
||||
"PB15": {"SPI2_CLK", "JTAG0_TCK"},
|
||||
"PB16": {"SPI2_MOSI", "JTAG0_TDO"},
|
||||
"PB17": {"SPI2_MISO", "JTAG0_TDI"},
|
||||
"PB18": {"I2C1_SCL"},
|
||||
"PB19": {"I2C1_SDA"},
|
||||
"PB20": {"I2C2_SCL"},
|
||||
"PB21": {"I2C2_SDA"},
|
||||
"PB22": {"UART0_TX", "IR1_TX"},
|
||||
"PB23": {"UART0_RX", "IR1_RX"},
|
||||
"PC0": {"NWE#", "SPI0_MOSI"},
|
||||
"PC1": {"NALE", "SPI0_MISO"},
|
||||
"PC2": {"NCLE", "SPI0_CLK"},
|
||||
"PC3": {"NCE1"},
|
||||
"PC4": {"NCE0"},
|
||||
"PC5": {"NRE#"},
|
||||
"PC6": {"NRB0", "SDC2_CMD"},
|
||||
"PC7": {"NRB1", "SDC2_CLK"},
|
||||
"PC8": {"NDQ0", "SDC2_D0"},
|
||||
"PC9": {"NDQ1", "SDC2_D1"},
|
||||
"PC10": {"NDQ2", "SDC2_D2"},
|
||||
"PC11": {"NDQ3", "SDC2_D3"},
|
||||
"PC12": {"NDQ4"},
|
||||
"PC13": {"NDQ5"},
|
||||
"PC14": {"NDQ6"},
|
||||
"PC15": {"NDQ7"},
|
||||
"PC16": {"NWP"},
|
||||
"PC17": {"NCE2"},
|
||||
"PC18": {"NCE3"},
|
||||
"PC19": {"NCE4", "SPI2_CS0", "", "", "PC_EINT12"},
|
||||
"PC20": {"NCE5", "SPI2_CLK", "", "", "PC_EINT13"},
|
||||
"PC21": {"NCE6", "SPI2_MOSI", "", "", "PC_EINT14"},
|
||||
"PC22": {"NCE7", "SPI2_MISO", "", "", "PC_EINT15"},
|
||||
"PC23": {"", "SPI2_CS0"},
|
||||
"PC24": {"NDQS"},
|
||||
"PD0": {"LCD0_D0", "LVDS0_VP0"},
|
||||
"PD1": {"LCD0_D1", "LVDS0_VN0"},
|
||||
"PD2": {"LCD0_D2", "LVDS0_VP1"},
|
||||
"PD3": {"LCD0_D3", "LVDS0_VN1"},
|
||||
"PD4": {"LCD0_D4", "LVDS0_VP2"},
|
||||
"PD5": {"LCD0_D5", "LVDS0_VN2"},
|
||||
"PD6": {"LCD0_D6", "LVDS0_VPC"},
|
||||
"PD7": {"LCD0_D7", "LVDS0_VNC"},
|
||||
"PD8": {"LCD0_D8", "LVDS0_VP3"},
|
||||
"PD9": {"LCD0_D9", "LVDS0_VN3"},
|
||||
"PD10": {"LCD0_D10", "LVDS1_VP0"},
|
||||
"PD11": {"LCD0_D11", "LVDS1_VN0"},
|
||||
"PD12": {"LCD0_D12", "LVDS1_VP1"},
|
||||
"PD13": {"LCD0_D13", "LVDS1_VN1"},
|
||||
"PD14": {"LCD0_D14", "LVDS1_VP2"},
|
||||
"PD15": {"LCD0_D15", "LVDS1_VN2"},
|
||||
"PD16": {"LCD0_D16", "LVDS1_VPC"},
|
||||
"PD17": {"LCD0_D17", "LVDS1_VNC"},
|
||||
"PD18": {"LCD0_D18", "LVDS1_VP3"},
|
||||
"PD19": {"LCD0_D19", "LVDS1_VN3"},
|
||||
"PD20": {"LCD0_D20", "CSI1_MCLK"},
|
||||
"PD21": {"LCD0_D21", "SMC_VPPEN"},
|
||||
"PD22": {"LCD0_D22", "SMC_VPPPP"},
|
||||
"PD23": {"LCD0_D23", "SMC_DET"},
|
||||
"PD24": {"LCD0_CLK", "SMC_VCCEN"},
|
||||
"PD25": {"LCD0_DE", "SMC_RST"},
|
||||
"PD26": {"LCD0_HSYNC", "SMC_SLK"},
|
||||
"PD27": {"LCD0_VSYNC", "SMC_SDA"},
|
||||
"PE0": {"TS0_CLK", "CSI0_PCLK"},
|
||||
"PE1": {"TS0_ERR", "CSI0_MCLK"},
|
||||
"PE2": {"TS0_SYNC", "CSI0_HSYNC"},
|
||||
"PE3": {"TS0_DLVD", "CSI0_VSYNC"},
|
||||
"PE4": {"TS0_D0", "CSI0_D0"},
|
||||
"PE5": {"TS0_D1", "CSI0_D1"},
|
||||
"PE6": {"TS0_D2", "CSI0_D2"},
|
||||
"PE7": {"TS0_D3", "CSI0_D3"},
|
||||
"PE8": {"TS0_D4", "CSI0_D4"},
|
||||
"PE9": {"TS0_D5", "CSI0_D5"},
|
||||
"PE10": {"TS0_D6", "CSI0_D6"},
|
||||
"PE11": {"TS0_D7", "CSI0_D7"},
|
||||
"PF0": {"SDC0_D1", "", "JTAG1_TMS"},
|
||||
"PF1": {"SDC0_D0", "", "JTAG1_TDI"},
|
||||
"PF2": {"SDC0_CLK", "", "UART0_TX"},
|
||||
"PF3": {"SDC0_CMD", "", "JTAG1_TDO"},
|
||||
"PF4": {"SDC0_D3", "", "UART0_RX"},
|
||||
"PF5": {"SDC0_D2", "", "JTAG1_TCK"},
|
||||
"PG0": {"TS1_CLK", "CSI1_PCLK", "SDC1_CMD"},
|
||||
"PG1": {"TS1_ERR", "CSI1_MCLK", "SDC1_CLK"},
|
||||
"PG2": {"TS1_SYNC", "CSI1_HSYNC", "SDC1_D0"},
|
||||
"PG3": {"TS1_DVLD", "CSI1_VSYNC", "SDC1_D1"},
|
||||
"PG4": {"TS1_D0", "CSI1_D0", "SDC1_D2", "CSI0_D8"},
|
||||
"PG5": {"TS1_D1", "CSI1_D1", "SDC1_D3", "CSI0_D9"},
|
||||
"PG6": {"TS1_D2", "CSI1_D2", "UART3_TX", "CSI0_D10"},
|
||||
"PG7": {"TS1_D3", "CSI1_D3", "UART3_RX", "CSI0_D11"},
|
||||
"PG8": {"TS1_D4", "CSI1_D4", "UART3_RTS", "CSI0_D12"},
|
||||
"PG9": {"TS1_D5", "CSI1_D4", "UART3_CTS", "CSI0_D13"},
|
||||
"PG10": {"TS1_D6", "CSI1_D6", "UART4_TX", "CSI0_D14"},
|
||||
"PG11": {"TS1_D7", "CSI1_D7", "UART4_RX", "CSI0_D15"},
|
||||
"PH0": {"LCD1_D0", "", "UART3_TX", "", "PH_EINT0"},
|
||||
"PH1": {"LCD1_D1", "", "UART3_RX", "", "PH_EINT1"},
|
||||
"PH2": {"LCD1_D2", "", "UART3_RTS", "", "PH_EINT2"},
|
||||
"PH3": {"LCD1_D3", "", "UART3_CTS", "", "PH_EINT3"},
|
||||
"PH4": {"LCD1_D4", "", "UART4_TX", "", "PH_EINT4"},
|
||||
"PH5": {"LCD1_D5", "", "UART4_RX", "", "PH_EINT5"},
|
||||
"PH6": {"LCD1_D6", "", "UART5_TX", "MS_BS", "PH_EINT6"},
|
||||
"PH7": {"LCD1_D7", "", "UART5_RX", "MS_CLK", "PH_EINT7"},
|
||||
"PH8": {"LCD1_D8", "ERXD3", "KP_IN0", "MS_D0", "PH_EINT8"},
|
||||
"PH9": {"LCD1_D9", "ERXD2", "KP_IN1", "MS_D1", "PH_EINT9"},
|
||||
"PH10": {"LCD1_D10", "ERXD1", "KP_IN2", "MS_D2", "PH_EINT10"},
|
||||
"PH11": {"LCD1_D11", "ERXD0", "KP_IN3", "MS_D3", "PH_EINT11"},
|
||||
"PH12": {"LCD1_D12", "", "PS2_SCK1", "", "PH_EINT12"},
|
||||
"PH13": {"LCD1_D13", "", "PS2_SDA1", "SMC_RST", "PH_EINT13"},
|
||||
"PH14": {"LCD1_D14", "ETXD3", "KP_IN4", "SMC_VPPEN", "PH_EINT14"},
|
||||
"PH15": {"LCD1_D15", "ETXD2", "KP_IN5", "SMC_VPPPP", "PH_EINT15"},
|
||||
"PH16": {"LCD1_D16", "ETXD1", "KP_IN6", "SMC_DET", "PH_EINT16"},
|
||||
"PH17": {"LCD1_D17", "ETXD0", "KP_IN7", "SMC_VCCEN", "PH_EINT17"},
|
||||
"PH18": {"LCD1_D18", "ERXCK", "KP_OUT0", "SMC_SLK", "PH_EINT18"},
|
||||
"PH19": {"LCD1_D19", "ERXERR", "KP_OUT1", "SMC_SDA", "PH_EINT19"},
|
||||
"PH20": {"LCD1_D20", "ERXDV", "CAN_TX", "", "PH_EINT20"},
|
||||
"PH21": {"LCD1_D21", "EMDC", "CAN_RX", "", "PH_EINT21"},
|
||||
"PH22": {"LCD1_D22", "EMDIO", "KP_OUT2", "SDC1_CMD", ""},
|
||||
"PH23": {"LCD1_D23", "ETXEN", "KP_OUT3", "SDC1_CLK", ""},
|
||||
"PH24": {"LCD1_CLK", "ETXCK", "KP_OUT4", "SDC1_D0", ""},
|
||||
"PH25": {"LCD1_DE", "ECRS", "KP_OUT5", "SDC1_D1", ""},
|
||||
"PH26": {"LCD1_HSYNC", "ECOL", "KP_OUT6", "SDC1_D2", ""},
|
||||
"PH27": {"LCD1_VSYNC", "ETXERR", "KP_OUT7", "SDC1_D3", ""},
|
||||
"PI0": {"", "I2C3_SCL"},
|
||||
"PI1": {"", "I2C3_SDA"},
|
||||
"PI2": {"", "I2C4_SCL"},
|
||||
"PI3": {"PWM1", "I2C4_SDA"},
|
||||
"PI4": {"SDC3_CMD"},
|
||||
"PI5": {"SDC3_CLK"},
|
||||
"PI6": {"SDC3_D0"},
|
||||
"PI7": {"SDC3_D1"},
|
||||
"PI8": {"SDC3_D2"},
|
||||
"PI9": {"SDC3_D3"},
|
||||
"PI10": {"SPI0_CS0", "UART5_TX", "", "PI_EINT22"},
|
||||
"PI11": {"SPI0_CLK", "UART5_RX", "", "PI_EINT23"},
|
||||
"PI12": {"SPI0_MOSI", "UART6_TX", "CLK_OUT_A", "PI_EINT24"},
|
||||
"PI13": {"SPI0_MISO", "UART6_RX", "CLK_OUT_B", "PI_EINT25"},
|
||||
"PI14": {"SPI0_CS0", "PS2_SCK1", "TCLKIN0", "PI_EINT26"},
|
||||
"PI15": {"SPI1_CS1", "PS2_SDA1", "TCLKIN1", "PI_EINT27"},
|
||||
"PI16": {"SPI1_CS0", "UART2_RTS", "", "PI_EINT28"},
|
||||
"PI17": {"SPI1_CLK", "UART2_CTS", "", "PI_EINT29"},
|
||||
"PI18": {"SPI1_MOSI", "UART2_TX", "", "PI_EINT30"},
|
||||
"PI19": {"SPI1_MISO", "UART2_RX", "", "PI_EINT31"},
|
||||
"PI20": {"PS2_SCK0", "UART7_TX", "HSCL"},
|
||||
"PI21": {"PS2_SDA0", "UART7_RX", "HSDA"},
|
||||
}
|
||||
|
||||
// mapA20Pins uses mappingA20 to actually set the altFunc fields of all gpio
|
||||
// and mark them as available.
|
||||
//
|
||||
// It is called by the generic allwinner processor code if an A20 is detected.
|
||||
func mapA20Pins() error {
|
||||
for name, altFuncs := range mappingA20 {
|
||||
pin := cpupins[name]
|
||||
pin.altFunc = altFuncs
|
||||
pin.available = true
|
||||
if strings.Contains(string(altFuncs[4]), "_EINT") ||
|
||||
strings.Contains(string(altFuncs[3]), "_EINT") {
|
||||
pin.supportEdge = true
|
||||
}
|
||||
|
||||
// Initializes the sysfs corresponding pin right away.
|
||||
pin.sysfsPin = sysfs.Pins[pin.Number()]
|
||||
}
|
||||
return nil
|
||||
}
|
174
vendor/periph.io/x/host/v3/allwinner/a64.go
generated
vendored
Normal file
174
vendor/periph.io/x/host/v3/allwinner/a64.go
generated
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// This file contains pin mapping information that is specific to the Allwinner
|
||||
// A64 model.
|
||||
|
||||
package allwinner
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"periph.io/x/conn/v3/pin"
|
||||
"periph.io/x/host/v3/sysfs"
|
||||
)
|
||||
|
||||
// A64 specific pins.
|
||||
var (
|
||||
X32KFOUT *pin.BasicPin // Clock output of 32Khz crystal
|
||||
KEY_ADC *pin.BasicPin // 6 bits resolution ADC for key application; can work up to 250Hz conversion rate; reference voltage is 2.0V
|
||||
EAROUTP *pin.BasicPin // Earpiece amplifier negative differential output
|
||||
EAROUTN *pin.BasicPin // Earpiece amplifier positive differential output
|
||||
)
|
||||
|
||||
//
|
||||
|
||||
func init() {
|
||||
X32KFOUT = &pin.BasicPin{N: "X32KFOUT"}
|
||||
// BUG(maruel): These need to be converted to an analog.PinIO implementation
|
||||
// once analog support is implemented.
|
||||
KEY_ADC = &pin.BasicPin{N: "KEY_ADC"}
|
||||
EAROUTP = &pin.BasicPin{N: "EAROUTP"}
|
||||
EAROUTN = &pin.BasicPin{N: "EAROUTN"}
|
||||
}
|
||||
|
||||
// mappingA64 describes the mapping of the A64 processor gpios to their
|
||||
// alternate functions.
|
||||
//
|
||||
// It omits the in & out functions which are available on all gpio.
|
||||
//
|
||||
// The mapping comes from the datasheet page 23:
|
||||
// http://files.pine64.org/doc/datasheet/pine64/A64_Datasheet_V1.1.pdf
|
||||
//
|
||||
// - The datasheet uses TWI instead of I2C but it is renamed here for
|
||||
// consistency.
|
||||
// - AIF is an audio interface, i.e. to connect to S/PDIF.
|
||||
// - RGMII means Reduced gigabit media-independent interface.
|
||||
// - SDC means SDCard?
|
||||
// - NAND connects to a NAND flash controller.
|
||||
// - CSI and CCI are for video capture.
|
||||
var mappingA64 = map[string][5]pin.Func{
|
||||
"PB0": {"UART2_TX", "", "JTAG0_TMS", "", "PB_EINT0"},
|
||||
"PB1": {"UART2_RX", "", "JTAG0_TCK", "SIM_PWREN", "PB_EINT1"},
|
||||
"PB2": {"UART2_RTS", "", "JTAG0_TDO", "SIM_VPPEN", "PB_EINT2"},
|
||||
"PB3": {"UART2_CTS", "I2S0_MCLK", "JTAG0_TDI", "SIM_VPPPP", "PB_EINT3"},
|
||||
"PB4": {"AIF2_SYNC", "I2S0_WS", "", "SIM_CLK", "PB_EINT4"},
|
||||
"PB5": {"AIF2_BCLK", "I2S0_SCK", "", "SIM_DATA", "PB_EINT5"},
|
||||
"PB6": {"AIF2_DOUT", "I2S0_DOUT", "", "SIM_RST", "PB_EINT6"},
|
||||
"PB7": {"AIF2_DIN", "I2S0_DIN", "", "SIM_DET", "PB_EINT7"},
|
||||
"PB8": {"", "", "UART0_TX", "", "PB_EINT8"},
|
||||
"PB9": {"", "", "UART0_RX", "", "PB_EINT9"},
|
||||
"PC0": {"NAND_WE", "", "SPI0_MOSI"},
|
||||
"PC1": {"NAND_ALE", "SDC2_DS", "SPI0_MISO"},
|
||||
"PC2": {"NAND_CLE", "", "SPI0_CLK"},
|
||||
"PC3": {"NAND_CE1", "", "SPI0_CS0"},
|
||||
"PC4": {"NAND_CE0"},
|
||||
"PC5": {"NAND_RE", "SDC2_CLK"},
|
||||
"PC6": {"NAND_RB0", "SDC2_CMD"},
|
||||
"PC7": {"NAND_RB1"},
|
||||
"PC8": {"NAND_DQ0", "SDC2_D0"},
|
||||
"PC9": {"NAND_DQ1", "SDC2_D1"},
|
||||
"PC10": {"NAND_DQ2", "SDC2_D2"},
|
||||
"PC11": {"NAND_DQ3", "SDC2_D3"},
|
||||
"PC12": {"NAND_DQ4", "SDC2_D4"},
|
||||
"PC13": {"NAND_DQ5", "SDC2_D5"},
|
||||
"PC14": {"NAND_DQ6", "SDC2_D6"},
|
||||
"PC15": {"NAND_DQ7", "SDC2_D7"},
|
||||
"PC16": {"NAND_DQS", "SDC2_RST"},
|
||||
"PD0": {"LCD_D2", "UART3_TX", "SPI1_CS0", "CCIR_CLK"},
|
||||
"PD1": {"LCD_D3", "UART3_RX", "SPI1_CLK", "CCIR_DE"},
|
||||
"PD2": {"LCD_D4", "UART4_TX", "SPI1_MOSI", "CCIR_HSYNC"},
|
||||
"PD3": {"LCD_D5", "UART4_RX", "SPI1_MISO", "CCIR_VSYNC"},
|
||||
"PD4": {"LCD_D6", "UART4_RTS", "", "CCIR_D0"},
|
||||
"PD5": {"LCD_D7", "UART4_CTS", "", "CCIR_D1"},
|
||||
"PD6": {"LCD_D10", "", "", "CCIR_D2"},
|
||||
"PD7": {"LCD_D11", "", "", "CCIR_D3"},
|
||||
"PD8": {"LCD_D12", "", "RGMII_RXD3", "CCIR_D4"},
|
||||
"PD9": {"LCD_D13", "", "RGMII_RXD2", "CCIR_D5"},
|
||||
"PD10": {"LCD_D14", "", "RGMII_RXD1"},
|
||||
"PD11": {"LCD_D15", "", "RGMII_RXD0"},
|
||||
"PD12": {"LCD_D18", "LVDS_VP0", "RGMII_RXCK"},
|
||||
"PD13": {"LCD_D19", "LVDS_VN0", "RGMII_RXCT"},
|
||||
"PD14": {"LCD_D20", "LVDS_VP1", "RGMII_RXER"},
|
||||
"PD15": {"LCD_D21", "LVDS_VN1", "RGMII_TXD3", "CCIR_D6"},
|
||||
"PD16": {"LCD_D22", "LVDS_VP2", "RGMII_TXD2", "CCIR_D7"},
|
||||
"PD17": {"LCD_D23", "LVDS_VN2", "RGMII_TXD1"},
|
||||
"PD18": {"LCD_CLK", "LVDS_VPC", "RGMII_TXD0"},
|
||||
"PD19": {"LCD_DE", "LVDS_VNC", "RGMII_TXCK"},
|
||||
"PD20": {"LCD_HSYNC", "LVDS_VP3", "RGMII_TXCT"},
|
||||
"PD21": {"LCD_VSYNC", "LVDS_VN3", "RGMII_CLKI"},
|
||||
"PD22": {"PWM0", "", "MDC"},
|
||||
"PD23": {"", "", "MDIO"},
|
||||
"PD24": {""},
|
||||
"PE0": {"CSI_PCLK", "", "TS_CLK"},
|
||||
"PE1": {"CSI_MCLK", "", "TS_ERR"},
|
||||
"PE2": {"CSI_HSYNC", "", "TS_SYNC"},
|
||||
"PE3": {"CSI_VSYNC", "", "TS_DVLD"},
|
||||
"PE4": {"CSI_D0", "", "TS_D0"},
|
||||
"PE5": {"CSI_D1", "", "TS_D1"},
|
||||
"PE6": {"CSI_D2", "", "TS_D2"},
|
||||
"PE7": {"CSI_D3", "", "TS_D3"},
|
||||
"PE8": {"CSI_D4", "", "TS_D4"},
|
||||
"PE9": {"CSI_D5", "", "TS_D5"},
|
||||
"PE10": {"CSI_D6", "", "TS_D6"},
|
||||
"PE11": {"CSI_D7", "", "TS_D7"},
|
||||
"PE12": {"CSI_SCK"},
|
||||
"PE13": {"CSI_SDA"},
|
||||
"PE14": {"PLL_LOCK_DBG", "I2C2_SCL"},
|
||||
"PE15": {"", "I2C2_SDA"},
|
||||
"PE16": {""},
|
||||
"PE17": {""},
|
||||
"PF0": {"SDC0_D1", "JTAG1_TMS"},
|
||||
"PF1": {"SDC0_D0", "JTAG1_TDI"},
|
||||
"PF2": {"SDC0_CLK", "UART0_TX"},
|
||||
"PF3": {"SDC0_CMD", "JTAG1_TDO"},
|
||||
"PF4": {"SDC0_D3", "UART0_RX"},
|
||||
"PF5": {"SDC0_D2", "JTAG1_TCK"},
|
||||
"PF6": {""},
|
||||
"PG0": {"SDC1_CLK", "", "", "", "PG_EINT0"},
|
||||
"PG1": {"SDC1_CMD", "", "", "", "PG_EINT1"},
|
||||
"PG2": {"SDC1_D0", "", "", "", "PG_EINT2"},
|
||||
"PG3": {"SDC1_D1", "", "", "", "PG_EINT3"},
|
||||
"PG4": {"SDC1_D2", "", "", "", "PG_EINT4"},
|
||||
"PG5": {"SDC1_D3", "", "", "", "PG_EINT5"},
|
||||
"PG6": {"UART1_TX", "", "", "", "PG_EINT6"},
|
||||
"PG7": {"UART1_RX", "", "", "", "PG_EINT7"},
|
||||
"PG8": {"UART1_RTS", "", "", "", "PG_EINT8"},
|
||||
"PG9": {"UART1_CTS", "", "", "", "PG_EINT9"},
|
||||
"PG10": {"AIF3_SYNC", "I2S1_WS", "", "", "PG_EINT10"},
|
||||
"PG11": {"AIF3_BCLK", "I2S1_SCK", "", "", "PG_EINT11"},
|
||||
"PG12": {"AIF3_DOUT", "I2S1_DOUT", "", "", "PG_EINT12"},
|
||||
"PG13": {"AIF3_DIN", "I2S1_DIN", "", "", "PG_EINT13"},
|
||||
"PH0": {"I2C0_SCL", "", "", "", "PH_EINT0"},
|
||||
"PH1": {"I2C0_SDA", "", "", "", "PH_EINT1"},
|
||||
"PH2": {"I2C1_SCL", "", "", "", "PH_EINT2"},
|
||||
"PH3": {"I2C1_SDA", "", "", "", "PH_EINT3"},
|
||||
"PH4": {"UART3_TX", "", "", "", "PH_EINT4"},
|
||||
"PH5": {"UART3_RX", "", "", "", "PH_EINT5"},
|
||||
"PH6": {"UART3_RTS", "", "", "", "PH_EINT6"},
|
||||
"PH7": {"UART3_CTS", "", "", "", "PH_EINT7"},
|
||||
"PH8": {"OWA_OUT", "", "", "", "PH_EINT8"},
|
||||
"PH9": {"", "", "", "", "PH_EINT9"},
|
||||
"PH10": {"MIC_CLK", "", "", "", "PH_EINT10"},
|
||||
"PH11": {"MIC_DATA", "", "", "", "PH_EINT11"},
|
||||
}
|
||||
|
||||
// mapA64Pins uses mappingA64 to actually set the altFunc fields of all gpio
|
||||
// and mark them as available.
|
||||
//
|
||||
// It is called by the generic allwinner processor code if an A64 is detected.
|
||||
func mapA64Pins() error {
|
||||
for name, altFuncs := range mappingA64 {
|
||||
pin := cpupins[name]
|
||||
pin.altFunc = altFuncs
|
||||
pin.available = true
|
||||
if strings.Contains(string(altFuncs[4]), "_EINT") {
|
||||
pin.supportEdge = true
|
||||
}
|
||||
|
||||
// Initializes the sysfs corresponding pin right away.
|
||||
pin.sysfsPin = sysfs.Pins[pin.Number()]
|
||||
}
|
||||
return nil
|
||||
}
|
7
vendor/periph.io/x/host/v3/allwinner/allwinner_arm.go
generated
vendored
Normal file
7
vendor/periph.io/x/host/v3/allwinner/allwinner_arm.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package allwinner
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/allwinner/allwinner_arm64.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/allwinner/allwinner_arm64.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build arm64
|
||||
|
||||
package allwinner
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/allwinner/allwinner_other.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/allwinner/allwinner_other.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm,!arm64
|
||||
|
||||
package allwinner
|
||||
|
||||
const isArm = false
|
281
vendor/periph.io/x/host/v3/allwinner/clock.go
generated
vendored
Normal file
281
vendor/periph.io/x/host/v3/allwinner/clock.go
generated
vendored
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package allwinner
|
||||
|
||||
const (
|
||||
clockSPIEnable clockSPI = 1 << 31 // SCLK_GATING
|
||||
// 30:26 reserved
|
||||
clockSPIOSC24M clockSPI = 0 << 24 // CLK_SRC_SEL
|
||||
clockSPIPLL6 clockSPI = 1 << 24 // A64: PLL_PERIPH0(1X)
|
||||
clockSPIPLL5 clockSPI = 2 << 24 // A64: PLL_PERIPH1(1X) R8: PLL5 = DDR
|
||||
// 23:18 reserved
|
||||
clockSPIDiv1a clockSPI = 0 << 16 // CLK_DIV_RATIO_N
|
||||
clockSPIDiv2a clockSPI = 1 << 16 //
|
||||
clockSPIDiv4a clockSPI = 2 << 16 //
|
||||
clockSPIDiv8a clockSPI = 3 << 16 //
|
||||
// 15:4 reserved
|
||||
clockSPIDiv1b clockSPI = 0 << 0 // CLK_DIV_RATIO_M
|
||||
clockSPIDiv2b clockSPI = 1 << 0 //
|
||||
clockSPIDiv3b clockSPI = 2 << 0 //
|
||||
clockSPIDiv4b clockSPI = 3 << 0 //
|
||||
clockSPIDiv5b clockSPI = 4 << 0 //
|
||||
clockSPIDiv6b clockSPI = 5 << 0 //
|
||||
clockSPIDiv7b clockSPI = 6 << 0 //
|
||||
clockSPIDiv8b clockSPI = 7 << 0 //
|
||||
clockSPIDiv9b clockSPI = 8 << 0 //
|
||||
clockSPIDiv10b clockSPI = 9 << 0 //
|
||||
clockSPIDiv11b clockSPI = 10 << 0 //
|
||||
clockSPIDiv12b clockSPI = 11 << 0 //
|
||||
clockSPIDiv13b clockSPI = 12 << 0 //
|
||||
clockSPIDiv14b clockSPI = 13 << 0 //
|
||||
clockSPIDiv15b clockSPI = 14 << 0 //
|
||||
clockSPIDiv16b clockSPI = 15 << 0 //
|
||||
)
|
||||
|
||||
// Also valid for IR.
|
||||
//
|
||||
// SPI0_SCLK_CFG_REG / SPI1_SCLK_CFG_REG / SPI2_SCLK_CFG_REG / IR_SCLK_CFG_REG
|
||||
//
|
||||
// A64: Page 110-111. (Also Page 554?)
|
||||
// R8: Page 71.
|
||||
type clockSPI uint32
|
||||
|
||||
const (
|
||||
clockPLL6Enable clockPLL6R8Ctl = 1 << 31 // PLL6_Enable
|
||||
clockPLL6Force24Mhz clockPLL6R8Ctl = 1 << 30 // PLL6_BYPASS_EN; force 24Mhz
|
||||
// 29:13 reserved
|
||||
clockPLL6FactorMulN0 clockPLL6R8Ctl = 0 << 8 // PLL6_FACTOR_N
|
||||
clockPLL6FactorMulN1 clockPLL6R8Ctl = 1 << 8 //
|
||||
clockPLL6FactorMulN2 clockPLL6R8Ctl = 2 << 8 //
|
||||
clockPLL6FactorMulN3 clockPLL6R8Ctl = 3 << 8 //
|
||||
clockPLL6FactorMulN4 clockPLL6R8Ctl = 4 << 8 //
|
||||
clockPLL6FactorMulN5 clockPLL6R8Ctl = 5 << 8 //
|
||||
clockPLL6FactorMulN6 clockPLL6R8Ctl = 6 << 8 //
|
||||
clockPLL6FactorMulN7 clockPLL6R8Ctl = 7 << 8 //
|
||||
clockPLL6FactorMulN8 clockPLL6R8Ctl = 8 << 8 //
|
||||
clockPLL6FactorMulN9 clockPLL6R8Ctl = 9 << 8 //
|
||||
clockPLL6FactorMulN10 clockPLL6R8Ctl = 10 << 8 //
|
||||
clockPLL6FactorMulN11 clockPLL6R8Ctl = 11 << 8 //
|
||||
clockPLL6FactorMulN12 clockPLL6R8Ctl = 12 << 8 //
|
||||
clockPLL6FactorMulN13 clockPLL6R8Ctl = 13 << 8 //
|
||||
clockPLL6FactorMulN14 clockPLL6R8Ctl = 14 << 8 //
|
||||
clockPLL6FactorMulN15 clockPLL6R8Ctl = 15 << 8 //
|
||||
clockPLL6FactorMulN16 clockPLL6R8Ctl = 16 << 8 //
|
||||
clockPLL6FactorMulN17 clockPLL6R8Ctl = 17 << 8 //
|
||||
clockPLL6FactorMulN18 clockPLL6R8Ctl = 18 << 8 //
|
||||
clockPLL6FactorMulN19 clockPLL6R8Ctl = 19 << 8 //
|
||||
clockPLL6FactorMulN20 clockPLL6R8Ctl = 20 << 8 //
|
||||
clockPLL6FactorMulN21 clockPLL6R8Ctl = 21 << 8 //
|
||||
clockPLL6FactorMulN22 clockPLL6R8Ctl = 22 << 8 //
|
||||
clockPLL6FactorMulN23 clockPLL6R8Ctl = 23 << 8 //
|
||||
clockPLL6FactorMulN24 clockPLL6R8Ctl = 24 << 8 //
|
||||
clockPLL6FactorMulN25 clockPLL6R8Ctl = 25 << 8 //
|
||||
clockPLL6FactorMulN26 clockPLL6R8Ctl = 26 << 8 //
|
||||
clockPLL6FactorMulN27 clockPLL6R8Ctl = 27 << 8 //
|
||||
clockPLL6FactorMulN28 clockPLL6R8Ctl = 28 << 8 //
|
||||
clockPLL6FactorMulN29 clockPLL6R8Ctl = 29 << 8 //
|
||||
clockPLL6FactorMulN30 clockPLL6R8Ctl = 30 << 8 //
|
||||
clockPLL6FactorMulN31 clockPLL6R8Ctl = 31 << 8 //
|
||||
clockPLL6Damping clockPLL6R8Ctl = 2 << 6 //
|
||||
clockPLL6FactorMulK1 clockPLL6R8Ctl = 0 << 4 // PLL6_FACTOR_K
|
||||
clockPLL6FactorMulK2 clockPLL6R8Ctl = 1 << 4 //
|
||||
clockPLL6FactorMulK3 clockPLL6R8Ctl = 2 << 4 //
|
||||
clockPLL6FactorMulK4 clockPLL6R8Ctl = 3 << 4 //
|
||||
// 3:2 reserved
|
||||
clockPLL6FactorDivM1 clockPLL6R8Ctl = 0 << 4 // PLL6_FACTOR_M
|
||||
clockPLL6FactorDivM2 clockPLL6R8Ctl = 1 << 4 //
|
||||
clockPLL6FactorDivM3 clockPLL6R8Ctl = 2 << 4 //
|
||||
clockPLL6FactorDivM4 clockPLL6R8Ctl = 3 << 4 //
|
||||
)
|
||||
|
||||
// PLL6_CFG_REG
|
||||
// R8: Page 63; default 0x21009931
|
||||
//
|
||||
// Output = (24MHz*N*K)/M/2
|
||||
// Note: the output 24MHz*N*K clock must be in the range of 240MHz~3GHz if the
|
||||
// bypass is disabled.
|
||||
type clockPLL6R8Ctl uint32
|
||||
|
||||
// clockMap is the mapping of important registers across CPUs.
|
||||
type clockMap struct {
|
||||
reserved0 [0xA0 / 4]uint32 //
|
||||
spi0Clk clockSPI // 0x0A0 SPI0_SCLK_CFG_REG SPI0 Clock
|
||||
spi1Clk clockSPI // 0x0A4 SPI1_SCLK_CFG_REG SPI1 Clock
|
||||
spi2Clk clockSPI // 0x0A8 SPI2_SCLK_CFG_REG SPI2 Clock (Not on A64)
|
||||
}
|
||||
|
||||
// R8: Page 57-59.
|
||||
type clockMapR8 struct {
|
||||
r0 uint32 // 0x000 PLL1_CFG_REG PLL1 Control
|
||||
r1 uint32 // 0x004 PLL1_TUN_REG PLL1 Tuning
|
||||
r2 uint32 // 0x008 PLL2_CFG_REG PLL2 Control
|
||||
r3 uint32 // 0x00C PLL2_TUN_REG PLL2 Tuning
|
||||
r4 uint32 // 0x010 PLL3_CFG_REG PLL3 Control
|
||||
r5 uint32 // 0x014
|
||||
r6 uint32 // 0x018 PLL4_CFG_REG PLL4 Control
|
||||
r7 uint32 // 0x01C
|
||||
r8 uint32 // 0x020 PLL5_CFG_REG PLL5 Control
|
||||
r9 uint32 // 0x024 PLL5_TUN_REG PLL5 Tuning
|
||||
r10 clockPLL6R8Ctl // 0x028 PLL6_CFG_REG PLL6 Control
|
||||
r11 uint32 // 0x02C PLL6 Tuning
|
||||
r12 uint32 // 0x030 PLL7_CFG_REG
|
||||
r13 uint32 // 0x034
|
||||
r14 uint32 // 0x038 PLL1_TUN2_REG PLL1 Tuning2
|
||||
r15 uint32 // 0x03C PLL5_TUN2_REG PLL5 Tuning2
|
||||
r16 uint32 // 0x04C
|
||||
r17 uint32 // 0x050 OSC24M_CFG_REG OSC24M control
|
||||
r18 uint32 // 0x054 CPU_AHB_APB0_CFG_REG CPU, AHB And APB0 Divide Ratio
|
||||
r19 uint32 // 0x058 APB1_CLK_DIV_REG APB1 Clock Divider
|
||||
r20 uint32 // 0x05C AXI_GATING_REG AXI Module Clock Gating
|
||||
r21 uint32 // 0x060 AHB_GATING_REG0 AHB Module Clock Gating 0
|
||||
r22 uint32 // 0x064 AHB_GATING_REG1 AHB Module Clock Gating 1
|
||||
r23 uint32 // 0x068 APB0_GATING_REG APB0 Module Clock Gating
|
||||
r24 uint32 // 0x06C APB1_GATING_REG APB1 Module Clock Gating
|
||||
r25 uint32 // 0x080 NAND_SCLK_CFG_REG Nand Flash Clock
|
||||
r26 uint32 // 0x084
|
||||
r27 uint32 // 0x088 SD0_SCLK_CFG_REG SD0 Clock
|
||||
r28 uint32 // 0x08C SD1_SCLK_CFG_REG SD1 Clock
|
||||
r29 uint32 // 0x090 SD2_SCLK_CFG_REG SD2 Clock
|
||||
r30 uint32 // 0x094
|
||||
r31 uint32 // 0x098
|
||||
r32 uint32 // 0x09C CE_SCLK_CFG_REG Crypto Engine Clock
|
||||
spi0Clk clockSPI // 0x0A0 SPI0_SCLK_CFG_REG SPI0 Clock
|
||||
spi1Clk clockSPI // 0x0A4 SPI1_SCLK_CFG_REG SPI1 Clock
|
||||
spi2Clk clockSPI // 0x0A8 SPI2_SCLK_CFG_REG SPI2 Clock
|
||||
r33 uint32 // 0x0AC
|
||||
irClk clockSPI // 0x0B0 IR_SCLK_CFG_REG IR Clock
|
||||
r34 uint32 // 0x0B4
|
||||
r35 uint32 // 0x0B8
|
||||
r36 uint32 // 0x0BC
|
||||
r37 uint32 // 0x0C0
|
||||
r38 uint32 // 0x0C4
|
||||
r39 uint32 // 0x0C8
|
||||
r40 uint32 // 0x0CC
|
||||
r41 uint32 // 0x0D0
|
||||
r42 uint32 // 0x0D4
|
||||
r43 uint32 // 0x100 DRAM_SCLK_CFG_REG DRAM Clock
|
||||
r44 uint32 // 0x104 BE_CFG_REG Display Engine Backend Clock
|
||||
r45 uint32 // 0x108
|
||||
r46 uint32 // 0x10C FE_CFG_REG Display Engine Front End Clock
|
||||
r47 uint32 // 0x110
|
||||
r48 uint32 // 0x114
|
||||
r49 uint32 // 0x118
|
||||
r50 uint32 // 0x11C
|
||||
r51 uint32 // 0x120
|
||||
r52 uint32 // 0x124
|
||||
r53 uint32 // 0x128
|
||||
r54 uint32 // 0x12C LCD_CH1_CFG_REG LCD Channel1 Clock
|
||||
r55 uint32 // 0x130
|
||||
r56 uint32 // 0x134 CSI_CFG_REG CSI Clock
|
||||
r57 uint32 // 0x138
|
||||
r58 uint32 // 0x13C VE_CFG_REG Video Engine Clock
|
||||
r59 uint32 // 0x140 AUDIO_CODEC_SCLK_CFG_REG Audio Codec Gating Special Clock
|
||||
r60 uint32 // 0x144 AVS_SCLK_CFG_REG AVS Gating Special Clock
|
||||
r61 uint32 // 0x148
|
||||
r62 uint32 // 0x14C
|
||||
r63 uint32 // 0x150
|
||||
r64 uint32 // 0x154 MALI_CLOCK_CFG_REG Mali400 Gating Special Clock
|
||||
r65 uint32 // 0x158
|
||||
r66 uint32 // 0x15C MBUS_SCLK_CFG_REG MBUS Gating Clock
|
||||
r67 uint32 // 0x160 IEP_SCLK_CFG_REG IEP Gating Clock
|
||||
}
|
||||
|
||||
// A64: Page 81-84.
|
||||
type clockMapA64 struct {
|
||||
r0 uint32 // 0x000 PLL_CPUX_CTRL_REG PLL_CPUX Control Register
|
||||
r1 uint32 // 0x008 PLL_AUDIO_CTRL_REG PLL_AUDIO Control Register
|
||||
r2 uint32 // 0x010 PLL_VIDEO0_CTRL_REG PLL_VIDEO0 Control Register
|
||||
r3 uint32 // 0x018 PLL_VE_CTRL_REG PLL_VE Control Register
|
||||
r4 uint32 // 0x020 PLL_DDR0_CTRL_REG PLL_DDR0 Control Register
|
||||
r5 uint32 // 0x028 PLL_PERIPH0_CTRL_REG PLL_PERIPH0 Control Register
|
||||
r6 uint32 // 0x02C PLL_PERIPH1_CTRL_REG PLL_PERIPH1 Control Register
|
||||
r7 uint32 // 0x030 PLL_VIDEO1_CTRL_REG PLL_VIDEO1 Control Register
|
||||
r8 uint32 // 0x038 PLL_GPU_CTRL_REG PLL_GPU Control Register
|
||||
r9 uint32 // 0x040 PLL_MIPI_CTRL_REG PLL_MIPI Control Register
|
||||
r10 uint32 // 0x044 PLL_HSIC_CTRL_REG PLL_HSIC Control Register
|
||||
r11 uint32 // 0x048 PLL_DE_CTRL_REG PLL_DE Control Register
|
||||
r12 uint32 // 0x04C PLL_DDR1_CTRL_REG PLL_DDR1 Control Register
|
||||
r13 uint32 // 0x050 CPU_AXI_CFG_REG CPUX/AXI Configuration Register
|
||||
r14 uint32 // 0x054 AHB1_APB1_CFG_REG AHB1/APB1 Configuration Register
|
||||
r15 uint32 // 0x058 APB2_CFG_REG APB2 Configuration Register
|
||||
r16 uint32 // 0x05C AHB2_CFG_REG AHB2 Configuration Register
|
||||
r17 uint32 // 0x060 BUS_CLK_GATING_REG0 Bus Clock Gating Register 0
|
||||
r18 uint32 // 0x064 BUS_CLK_GATING_REG1 Bus Clock Gating Register 1
|
||||
r19 uint32 // 0x068 BUS_CLK_GATING_REG2 Bus Clock Gating Register 2
|
||||
r20 uint32 // 0x06C BUS_CLK_GATING_REG3 Bus Clock Gating Register 3
|
||||
r21 uint32 // 0x070 BUS_CLK_GATING_REG4 Bus Clock Gating Register 4
|
||||
r22 uint32 // 0x074 THS_CLK_REG THS Clock Register
|
||||
r23 uint32 // 0x080 NAND_CLK_REG NAND Clock Register
|
||||
r24 uint32 // 0x088 SDMMC0_CLK_REG SDMMC0 Clock Register
|
||||
r25 uint32 // 0x08C SDMMC1_CLK_REG SDMMC1 Clock Register
|
||||
r26 uint32 // 0x090 SDMMC2_CLK_REG SDMMC2 Clock Register
|
||||
r27 uint32 // 0x098 TS_CLK_REG TS Clock Register
|
||||
r28 uint32 // 0x09C CE_CLK_REG CE Clock Register
|
||||
spi0Clk clockSPI // 0x0A0 SPI0_CLK_REG SPI0 Clock Register
|
||||
spi1Clk clockSPI // 0x0A4 SPI1_CLK_REG SPI1 Clock Register
|
||||
r29 uint32 // 0x0B0 I2S/PCM-0_CLK_REG I2S/PCM-0 Clock Register
|
||||
r30 uint32 // 0x0B4 I2S/PCM-1_CLK_REG I2S/PCM-1 Clock Register
|
||||
r31 uint32 // 0x0B8 I2S/PCM-2_CLK_REG I2S/PCM-2 Clock Register
|
||||
r32 uint32 // 0x0C0 SPDIF_CLK_REG SPDIF Clock Register
|
||||
r33 uint32 // 0x0CC USBPHY_CFG_REG USBPHY Configuration Register
|
||||
r34 uint32 // 0x0F4 DRAM_CFG_REG DRAM Configuration Register
|
||||
r35 uint32 // 0x0F8 PLL_DDR_CFG_REG PLL_DDR Configuration Register
|
||||
r36 uint32 // 0x0FC MBUS_RST_REG MBUS Reset Register
|
||||
r37 uint32 // 0x100 DRAM_CLK_GATING_REG DRAM Clock Gating Register
|
||||
r38 uint32 // 0x104 DE_CLK_REG DE Clock Register
|
||||
r39 uint32 // 0x118 TCON0_CLK_REG TCON0 Clock Register
|
||||
r40 uint32 // 0x11C TCON1_CLK_REG TCON1 Clock Register
|
||||
r41 uint32 // 0x124 DEINTERLACE_CLK_REG DEINTERLACE Clock Register
|
||||
r42 uint32 // 0x130 CSI_MISC_CLK_REG CSI_MISC Clock Register
|
||||
r43 uint32 // 0x134 CSI_CLK_REG CSI Clock Register
|
||||
r44 uint32 // 0x13C VE_CLK_REG VE Clock Register
|
||||
r45 uint32 // 0x140 AC_DIG_CLK_REG AC Digital Clock Register
|
||||
r46 uint32 // 0x144 AVS_CLK_REG AVS Clock Register
|
||||
r47 uint32 // 0x150 HDMI_CLK_REG HDMI Clock Register
|
||||
r48 uint32 // 0x154 HDMI_SLOW_CLK_REG HDMI Slow Clock Register
|
||||
r49 uint32 // 0x15C MBUS_CLK_REG MBUS Clock Register
|
||||
r50 uint32 // 0x168 MIPI_DSI_CLK_REG MIPI_DSI Clock Register
|
||||
r51 uint32 // 0x1A0 GPU_CLK_REG GPU Clock Register
|
||||
r52 uint32 // 0x200 PLL_STABLE_TIME_REG0 PLL Stable Time Register0
|
||||
r53 uint32 // 0x204 PLL_STABLE_TIME_REG1 PLL Stable Time Register1
|
||||
r54 uint32 // 0x21C PLL_PERIPH1_BIAS_REG PLL_PERIPH1 Bias Register
|
||||
r55 uint32 // 0x220 PLL_CPUX_BIAS_REG PLL_CPUX Bias Register
|
||||
r56 uint32 // 0x224 PLL_AUDIO_BIAS_REG PLL_AUDIO Bias Register
|
||||
r57 uint32 // 0x228 PLL_VIDEO0_BIAS_REG PLL_VIDEO0 Bias Register
|
||||
r58 uint32 // 0x22C PLL_VE_BIAS_REG PLL_VE Bias Register
|
||||
r59 uint32 // 0x230 PLL_DDR0_BIAS_REG PLL_DDR0 Bias Register
|
||||
r60 uint32 // 0x234 PLL_PERIPH0_BIAS_REG PLL_PERIPH0 Bias Register
|
||||
r61 uint32 // 0x238 PLL_VIDEO1_BIAS_REG PLL_VIDEO1 Bias Register
|
||||
r62 uint32 // 0x23C PLL_GPU_BIAS_REG PLL_GPU Bias Register
|
||||
r63 uint32 // 0x240 PLL_MIPI_BIAS_REG PLL_MIPI Bias Register
|
||||
r64 uint32 // 0x244 PLL_HSIC_BIAS_REG PLL_HSIC Bias Register
|
||||
r65 uint32 // 0x248 PLL_DE_BIAS_REG PLL_DE Bias Register
|
||||
r66 uint32 // 0x24C PLL_DDR1_BIAS_REG PLL_DDR1 Bias Register
|
||||
r67 uint32 // 0x250 PLL_CPUX_TUN_REG PLL_CPUX Tuning Register
|
||||
r68 uint32 // 0x260 PLL_DDR0_TUN_REG PLL_DDR0 Tuning Register
|
||||
r69 uint32 // 0x270 PLL_MIPI_TUN_REG PLL_MIPI Tuning Register
|
||||
r70 uint32 // 0x27C PLL_PERIPH1_PAT_CTRL_REG PLL_PERIPH1 Pattern Control Register
|
||||
r71 uint32 // 0x280 PLL_CPUX_PAT_CTRL_REG PLL_CPUX Pattern Control Register
|
||||
r72 uint32 // 0x284 PLL_AUDIO_PAT_CTRL_REG PLL_AUDIO Pattern Control Register
|
||||
r73 uint32 // 0x288 PLL_VIDEO0_PAT_CTRL_REG PLL_VIDEO0 Pattern Control Register
|
||||
r74 uint32 // 0x28C PLL_VE_PAT_CTRL_REG PLL_VE Pattern Control Register
|
||||
r75 uint32 // 0x290 PLL_DDR0_PAT_CTRL_REG PLL_DDR0 Pattern Control Register
|
||||
r76 uint32 // 0x298 PLL_VIDEO1_PAT_CTRL_REG PLL_VIDEO1 Pattern Control Register
|
||||
r77 uint32 // 0x29C PLL_GPU_PAT_CTRL_REG PLL_GPU Pattern Control Register
|
||||
r78 uint32 // 0x2A0 PLL_MIPI_PAT_CTRL_REG PLL_MIPI Pattern Control Register
|
||||
r79 uint32 // 0x2A4 PLL_HSIC_PAT_CTRL_REG PLL_HSIC Pattern Control Register
|
||||
r80 uint32 // 0x2A8 PLL_DE_PAT_CTRL_REG PLL_DE Pattern Control Register
|
||||
r81 uint32 // 0x2AC PLL_DDR1_PAT_CTRL_REG0 PLL_DDR1 Pattern Control Register0
|
||||
r82 uint32 // 0x2B0 PLL_DDR1_PAT_CTRL_REG1 PLL_DDR1 Pattern Control Register1
|
||||
r83 uint32 // 0x2C0 BUS_SOFT_RST_REG0 Bus Software Reset Register 0
|
||||
r84 uint32 // 0x2C4 BUS_SOFT_RST_REG1 Bus Software Reset Register 1
|
||||
r85 uint32 // 0x2C8 BUS_SOFT_RST_REG2 Bus Software Reset Register 2
|
||||
r86 uint32 // 0x2D0 BUS_SOFT_RST_REG3 Bus Software Reset Register 3
|
||||
r87 uint32 // 0x2D8 BUS_SOFT_RST_REG4 Bus Software Reset Register 4
|
||||
r88 uint32 // 0x2F0 CCM_SEC_SWITCH_REG CCM Security Switch Register
|
||||
r89 uint32 // 0x300 PS_CTRL_REG PS Control Register
|
||||
r90 uint32 // 0x304 PS_CNT_REG PS Counter Register
|
||||
r91 uint32 // 0x320 PLL_LOCK_CTRL_REG PLL Lock Control Register
|
||||
}
|
98
vendor/periph.io/x/host/v3/allwinner/detect.go
generated
vendored
Normal file
98
vendor/periph.io/x/host/v3/allwinner/detect.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package allwinner
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"periph.io/x/host/v3/distro"
|
||||
)
|
||||
|
||||
// Present detects whether the host CPU is an Allwinner CPU.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Allwinner_Technology
|
||||
func Present() bool {
|
||||
detection.do()
|
||||
return detection.isAllwinner
|
||||
}
|
||||
|
||||
// IsR8 detects whether the host CPU is an Allwinner R8 CPU.
|
||||
//
|
||||
// It looks for the string "sun5i-r8" in /proc/device-tree/compatible.
|
||||
func IsR8() bool {
|
||||
detection.do()
|
||||
return detection.isR8
|
||||
}
|
||||
|
||||
// IsA20 detects whether the host CPU is an Allwinner A20 CPU.
|
||||
//
|
||||
// It first looks for the string "sun71-a20" in /proc/device-tree/compatible,
|
||||
// and if that fails it checks for "Hardware : sun7i" in /proc/cpuinfo.
|
||||
func IsA20() bool {
|
||||
detection.do()
|
||||
return detection.isA20
|
||||
}
|
||||
|
||||
// IsA64 detects whether the host CPU is an Allwinner A64 CPU.
|
||||
//
|
||||
// It looks for the string "sun50iw1p1" in /proc/device-tree/compatible.
|
||||
func IsA64() bool {
|
||||
detection.do()
|
||||
return detection.isA64
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
type detectionS struct {
|
||||
mu sync.Mutex
|
||||
done bool
|
||||
isAllwinner bool
|
||||
isR8 bool
|
||||
isA20 bool
|
||||
isA64 bool
|
||||
}
|
||||
|
||||
var detection detectionS
|
||||
|
||||
// do contains the CPU detection logic that determines whether we have an
|
||||
// Allwinner CPU and if so, which exact model.
|
||||
//
|
||||
// Sadly there is no science behind this, it's more of a trial and error using
|
||||
// as many boards and OS flavors as possible.
|
||||
func (d *detectionS) do() {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if !d.done {
|
||||
d.done = true
|
||||
if isArm {
|
||||
for _, c := range distro.DTCompatible() {
|
||||
if strings.Contains(c, "sun50iw1p1") {
|
||||
d.isA64 = true
|
||||
}
|
||||
if strings.Contains(c, "sun5i-r8") {
|
||||
d.isR8 = true
|
||||
}
|
||||
if strings.Contains(c, "sun7i-a20") {
|
||||
d.isA20 = true
|
||||
}
|
||||
}
|
||||
d.isAllwinner = d.isA64 || d.isR8 || d.isA20
|
||||
|
||||
if !d.isAllwinner {
|
||||
// The kernel in the image that comes pre-installed on the pcDuino3 Nano
|
||||
// is an old 3.x kernel that doesn't expose the device-tree in procfs,
|
||||
// so do an extra check in cpuinfo as well if we haven't detected
|
||||
// anything yet.
|
||||
// Distros based on 4.x kernels do expose it.
|
||||
if hw, ok := distro.CPUInfo()["Hardware"]; ok {
|
||||
if hw == "sun7i" {
|
||||
d.isA20 = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
474
vendor/periph.io/x/host/v3/allwinner/dma.go
generated
vendored
Normal file
474
vendor/periph.io/x/host/v3/allwinner/dma.go
generated
vendored
Normal file
@ -0,0 +1,474 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Unlike the bcm283x, the allwinner CPUs do not have a "clear bit" and "set
|
||||
// bit" registers, they only have the data register. Also, allwinner CPUs do
|
||||
// not support linked lists of DMA buffers. On the other hand, the Allwinner DMA
|
||||
// controller supports 8 bits transfers instead of 32-128 bits that the bcm283x
|
||||
// DMA controller supports.
|
||||
//
|
||||
// This means that only 8 bits can be used per sample, and only one stream is
|
||||
// necessary. This results in 1/8th th memory usage than on the bcm283x. The
|
||||
// drawback is that a block of 8 contiguous GPIO pins must be dedicated to the
|
||||
// stream.
|
||||
|
||||
package allwinner
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"periph.io/x/conn/v3/driver/driverreg"
|
||||
"periph.io/x/host/v3/pmem"
|
||||
)
|
||||
|
||||
// dmaMap represents the DMA memory mapped CPU registers.
|
||||
//
|
||||
// This map is specific to the currently supported CPUs and will have to be
|
||||
// adapted as more CPUs are supported. In particular the number of physical
|
||||
// channels varies across different CPUs.
|
||||
//
|
||||
// Note that we modify the DMA controllers without telling the kernel driver.
|
||||
// The driver keeps its own table of which DMA channel is available so this
|
||||
// code could effectively crash the whole system. It practice this works.
|
||||
// #everythingisfine
|
||||
type dmaMap struct {
|
||||
irqEn dmaR8Irq // DMA_IRQ_EN_REG
|
||||
irqPendStas dmaR8PendingIrq // DMA_IRQ_PEND_STAS_REG
|
||||
reserved0 [(0x100 - 8) / 4]uint32 //
|
||||
normal [8]dmaR8NormalGroup // 0x100 The "8" "normal" DMA channels (only one active at a time so there's effectively one)
|
||||
reserved1 [0x100 / 4]uint32 //
|
||||
dedicated [8]dmaDedicatedGroup // 0x300 The 8 "dedicated" (as in actually existing) DMA channels
|
||||
}
|
||||
|
||||
func (d *dmaMap) getDedicated() int {
|
||||
for i := len(d.dedicated) - 1; i >= 0; i-- {
|
||||
if d.dedicated[i].isAvailable() {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// dmaNormalGroup is the control registers for the first block of 8 DMA
|
||||
// controllers.
|
||||
//
|
||||
// They can be intentionally slowed down, unlike the dedicated DMA ones.
|
||||
//
|
||||
// The big caveat is that only one controller can be active at a time and the
|
||||
// execution sequence is in accordance with the priority level. This means that
|
||||
// two normal DMA cannot be used to do simultaneous read and write. This
|
||||
// feature is critical for bus bitbanging.
|
||||
type dmaR8NormalGroup struct {
|
||||
cfg ndmaR8Cfg // NDMA_CTRL_REG
|
||||
srcAddr uint32 // NDMA_SRC_ADDR_REG
|
||||
dstAddr uint32 // NDMA_DEST_ADDR_REG
|
||||
byteCounter uint32 // NDMA_BC_REG
|
||||
reserved [4]uint32 //
|
||||
}
|
||||
|
||||
func (d *dmaR8NormalGroup) isAvailable() bool {
|
||||
return d.cfg == 0 && d.srcAddr == 0 && d.dstAddr == 0 && d.byteCounter == 0
|
||||
}
|
||||
|
||||
func (d *dmaR8NormalGroup) release() error {
|
||||
d.srcAddr = 0
|
||||
d.dstAddr = 0
|
||||
d.byteCounter = 0
|
||||
d.cfg = ndmaLoad
|
||||
//drvDMA.dmaMemory.irqEn &^= ...
|
||||
//drvDMA.dmaMemory.irqPendStas &^= ...
|
||||
return nil
|
||||
}
|
||||
|
||||
// dmaNormalGroup is the control registers for the second block of 8 DMA
|
||||
// controllers.
|
||||
//
|
||||
// They support different DReq and can do non-linear streaming.
|
||||
type dmaDedicatedGroup struct {
|
||||
cfg ddmaR8Cfg // DDMA_CTRL_REG
|
||||
srcAddr uint32 // DDMA_SRC_ADDR_REG
|
||||
dstAddr uint32 // DDMA_DEST_ADDR_REG
|
||||
byteCounter uint32 // DDMA_BC_REG (24 bits)
|
||||
reserved0 [2]uint32 //
|
||||
param ddmaR8Param // DDMA_PARA_REG (dedicated DMA only)
|
||||
reserved1 uint32 //
|
||||
}
|
||||
|
||||
func (d *dmaDedicatedGroup) isAvailable() bool {
|
||||
return d.cfg == 0 && d.srcAddr == 0 && d.dstAddr == 0 && d.byteCounter == 0 && d.param == 0
|
||||
}
|
||||
|
||||
func (d *dmaDedicatedGroup) set(srcAddr, dstAddr, l uint32, srcIO, dstIO bool, src ddmaR8Cfg) {
|
||||
d.srcAddr = srcAddr
|
||||
d.dstAddr = dstAddr
|
||||
d.byteCounter = l
|
||||
// TODO(maruel): Slow down the clock by another 2*250x
|
||||
//d.param = ddmaR8Param(250 | 250<<16)
|
||||
d.param = ddmaR8Param(1<<24 | 1<<8 | 1)
|
||||
// All these have value 0. This statement only exist for documentation.
|
||||
cfg := ddmaDstWidth8 | ddmaDstBurst1 | ddmaDstLinear | ddmaSrcWidth8 | ddmaSrcLinear | ddmaSrcBurst1
|
||||
cfg |= src | ddmaBCRemain
|
||||
if srcIO {
|
||||
cfg |= ddmaSrcIOMode
|
||||
} else if dstIO {
|
||||
cfg |= ddmaDstIOMode
|
||||
}
|
||||
d.cfg = ddmaLoad | cfg
|
||||
for i := 0; d.cfg&ddmaLoad != 0 && i < 100000; i++ {
|
||||
}
|
||||
if d.cfg&ddmaLoad != 0 {
|
||||
log.Printf("failed to load DDMA: %# v\n", d)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dmaDedicatedGroup) release() error {
|
||||
d.param = 0
|
||||
d.srcAddr = 0
|
||||
d.dstAddr = 0
|
||||
d.byteCounter = 0
|
||||
d.cfg = ddmaLoad
|
||||
//drvDMA.dmaMemory.irqEn &^= ...
|
||||
//drvDMA.dmaMemory.irqPendStas &^= ...
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
// 31 reserved
|
||||
dma7QueueEndIrq dmaA64Irq = 1 << 30 // DMA7_END_IRQ_EN; DMA 7 Queue End Transfer Interrupt Enable.
|
||||
dma7PackageEndIrq dmaA64Irq = 1 << 29 // DMA7_PKG_IRQ_EN; DMA 7 Package End Transfer Interrupt Enable.
|
||||
dma7HalfIrq dmaA64Irq = 1 << 28 // DMA7_HLAF_IRQ_EN; DMA 7 Half Package Transfer Interrupt Enable.
|
||||
// ...
|
||||
// 3 reserved
|
||||
dma0QueueEndIrq dmaA64Irq = 1 << 2 // DMA0_END_IRQ_EN; DMA 0 Queue End Transfer Interrupt Enable.
|
||||
dma0PackageEndIrq dmaA64Irq = 1 << 1 // DMA0_PKG_IRQ_EN; DMA 0 Package End Transfer Interrupt Enable.
|
||||
dma0HalfIrq dmaA64Irq = 1 << 0 // DMA0_HLAF_IRQ_EN; DMA 0 Half Package Transfer Interrupt Enable.
|
||||
)
|
||||
|
||||
// DMA_IRQ_EN_REG
|
||||
// A64: Page 199-201.
|
||||
type dmaA64Irq uint32
|
||||
|
||||
const (
|
||||
ddma7EndIrq dmaR8Irq = 1 << 31 // DDMA7_END_IRQ_EN
|
||||
ddma7HalfIreq dmaR8Irq = 1 << 30 // DDMA7_HF_IRQ_EN
|
||||
// ...
|
||||
ddma0EndIrq dmaR8Irq = 1 << 17 // DDMA0_END_IRQ_EN
|
||||
ddma0HalfIreq dmaR8Irq = 1 << 16 // DDMA0_HF_IRQ_EN
|
||||
ndma7EndIrq dmaR8Irq = 1 << 15 // NDMA7_END_IRQ_EN
|
||||
ndma7HalfIreq dmaR8Irq = 1 << 16 // NDDMA7_HF_IRQ_EN
|
||||
// ...
|
||||
ndma0EndIrq dmaR8Irq = 1 << 1 // NDMA0_END_IRQ_EN
|
||||
ndma0HFIreq dmaR8Irq = 1 << 0 // NDMA0_HF_IRQ_EN
|
||||
)
|
||||
|
||||
// DMA_IRQ_EN_REG
|
||||
// R8: Page 124-126.
|
||||
type dmaR8Irq uint32
|
||||
|
||||
const (
|
||||
// 31 reserved
|
||||
dma7QueueEndIrqPend dmaA64PendingIrq = 1 << 30 // DMA7_QUEUE_IRQ_PEND; DMA 7 Queue End Transfer Interrupt Pending. Set 1 to the bit will clear it.
|
||||
dma7PackageEndIrqPend dmaA64PendingIrq = 1 << 29 // DMA7_PKG_IRQ_PEND; DMA 7 Package End Transfer Interrupt Pending. Set 1 to the bit will clear it.
|
||||
dma7HalfIrqPend dmaA64PendingIrq = 1 << 28 // DMA7_HLAF_IRQ_PEND; DMA 7 Half Package Transfer Interrupt Pending. Set 1 to the bit will clear it.
|
||||
// ...
|
||||
// 3 reserved
|
||||
dma0QueueEndIrqPend dmaA64PendingIrq = 1 << 2 // DMA0_QUEUE_IRQ_PEND; DMA 0 Queue End Transfer Interrupt Pending. Set 1 to the bit will clear it.
|
||||
dma0PackageEndIrqPend dmaA64PendingIrq = 1 << 1 // DMA0_PKG_IRQ_PEND; DMA 0 Package End Transfer Interrupt Pending. Set 1 to the bit will clear it.
|
||||
dma0HalfIrqPend dmaA64PendingIrq = 1 << 0 // DMA0_HLAF_IRQ_PEND; DMA 0 Half Package Transfer Interrupt Pending. Set 1 to the bit will clear it.
|
||||
)
|
||||
|
||||
// DMA_IRQ_PEND_REG0
|
||||
// A64: Page 201-203.
|
||||
type dmaA64PendingIrq uint32
|
||||
|
||||
const (
|
||||
ddma7EndIrqPend dmaR8PendingIrq = 1 << 31 // DDMA7_END_IRQ_PEND
|
||||
ddma7HalfIreqPend dmaR8PendingIrq = 1 << 30 // DDMA7_HF_IRQ_PEND
|
||||
// ...
|
||||
ddma0EndIrqPend dmaR8PendingIrq = 1 << 17 // DDMA0_END_IRQ_PEND
|
||||
ddma0HalfIreqPend dmaR8PendingIrq = 1 << 16 // DDMA0_HF_IRQ_PEND
|
||||
ndma7EndIrqPend dmaR8PendingIrq = 1 << 15 // NDMA7_END_IRQ_PEND
|
||||
ndma7HalfIreqPend dmaR8PendingIrq = 1 << 16 // NDDMA7_HF_IRQ_PEND
|
||||
// ...
|
||||
ndma0EndIrqPend dmaR8PendingIrq = 1 << 1 // NDMA0_END_IRQ_PEND
|
||||
ndma0HalfIreqPend dmaR8PendingIrq = 1 << 0 // NDMA0_HF_IRQ_PEND
|
||||
)
|
||||
|
||||
// DMA_IRQ_PEND_STAS_REG
|
||||
// R8: Page 126-129.
|
||||
type dmaR8PendingIrq uint32
|
||||
|
||||
const (
|
||||
ndmaLoad ndmaR8Cfg = 1 << 31 // NDMA_LOAD
|
||||
ndmaContinuous ndmaR8Cfg = 1 << 30 // NDMA_CONTI_EN Continuous mode
|
||||
ndmaWaitClk0 ndmaR8Cfg = 0 << 27 // NDMA_WAIT_STATE Number of clock to wait for
|
||||
ndmaWaitClk2 ndmaR8Cfg = 1 << 27 // 2(n+1)
|
||||
ndmaWaitClk6 ndmaR8Cfg = 2 << 27 //
|
||||
ndmaWaitClk8 ndmaR8Cfg = 3 << 27 //
|
||||
ndmaWaitClk10 ndmaR8Cfg = 4 << 27 //
|
||||
ndmaWaitClk12 ndmaR8Cfg = 5 << 27 //
|
||||
ndmaWaitClk14 ndmaR8Cfg = 6 << 27 //
|
||||
ndmaWaitClk16 ndmaR8Cfg = 7 << 27 //
|
||||
ndmaDstWidth32 ndmaR8Cfg = 2 << 25 // NDMA_DST_DATA_WIDTH
|
||||
ndmaDstWidth16 ndmaR8Cfg = 1 << 25 //
|
||||
ndmaDstWidth8 ndmaR8Cfg = 0 << 25 //
|
||||
ndmaDstBurst8 ndmaR8Cfg = 2 << 23 // NDMA_DST_BST_LEN
|
||||
ndmaDstBurst4 ndmaR8Cfg = 1 << 23 //
|
||||
ndmaDstBurst1 ndmaR8Cfg = 0 << 23 //
|
||||
// 22 reserved NDMA_CFG_DST_NON_SECURE ?
|
||||
ndmaDstAddrNoInc ndmaR8Cfg = 1 << 21 // NDMA_DST_ADDR_TYPE
|
||||
ndmaDstDrqIRTX ndmaR8Cfg = 0 << 16 // NDMA_DST_DRQ_TYPE
|
||||
ndmaDstDrqUART1TX ndmaR8Cfg = 9 << 16 //
|
||||
ndmaDstDrqUART3TX ndmaR8Cfg = 11 << 16 //
|
||||
ndmaDstDrqAudio ndmaR8Cfg = 19 << 16 // 24.576MHz (Page 53)
|
||||
ndmaDstDrqSRAM ndmaR8Cfg = 21 << 16 //
|
||||
ndmaDstDrqSPI0TX ndmaR8Cfg = 24 << 16 //
|
||||
ndmaDstDrqSPI1TX ndmaR8Cfg = 25 << 16 //
|
||||
ndmaDstDrqSPI2TX ndmaR8Cfg = 26 << 16 //
|
||||
ndmaDstDrqUSB1 ndmaR8Cfg = 27 << 16 // 480MHz
|
||||
ndmaDstDrqUSB2 ndmaR8Cfg = 28 << 16 //
|
||||
ndmaDstDrqUSB3 ndmaR8Cfg = 29 << 16 //
|
||||
ndmaDstDrqUSB4 ndmaR8Cfg = 30 << 16 //
|
||||
ndmaDstDrqUSB5 ndmaR8Cfg = 31 << 16 //
|
||||
ndmaBCRemain ndmaR8Cfg = 1 << 15 // BC_MODE_SEL
|
||||
// 14:11 reserved
|
||||
ndmaSrcWidth32 ndmaR8Cfg = 2 << 9 // NDMA_SRC_DATA_WIDTH
|
||||
ndmaSrcWidth16 ndmaR8Cfg = 1 << 9 //
|
||||
ndmaSrcWidth8 ndmaR8Cfg = 0 << 9 //
|
||||
ndmaSrcBurst8 ndmaR8Cfg = 2 << 7 // NDMA_SRC_BST_LEN
|
||||
ndmaSrcBurst4 ndmaR8Cfg = 1 << 7 //
|
||||
ndmaSrcBurst1 ndmaR8Cfg = 0 << 7 //
|
||||
// 6 reserved NDMA_CFG_SRC_NON_SECURE ?
|
||||
ndmaSrcAddrNoInc ndmaR8Cfg = 1 << 5 // NDMA_SRC_ADDR_TYPE
|
||||
ndmaSrcDrqIRTX ndmaR8Cfg = 0 << 0 // NDMA_SRC_DRQ_TYPE
|
||||
ndmaSrcDrqUART1RX ndmaR8Cfg = 9 << 0 //
|
||||
ndmaSrcDrqUART3RX ndmaR8Cfg = 11 << 0 //
|
||||
ndmaSrcDrqAudio ndmaR8Cfg = 19 << 0 // 24.576MHz (Page 53)
|
||||
ndmaSrcDrqSRAM ndmaR8Cfg = 21 << 0 //
|
||||
ndmaSrcDrqSDRAM ndmaR8Cfg = 22 << 0 // 0~400MHz
|
||||
ndmaSrcDrqTPAD ndmaR8Cfg = 23 << 0 //
|
||||
ndmaSrcDrqSPI0RX ndmaR8Cfg = 24 << 0 //
|
||||
ndmaSrcDrqSPI1RX ndmaR8Cfg = 25 << 0 //
|
||||
ndmaSrcDrqSPI2RX ndmaR8Cfg = 26 << 0 //
|
||||
ndmaSrcDrqUSB1 ndmaR8Cfg = 27 << 0 // 480MHz
|
||||
ndmaSrcDrqUSB2 ndmaR8Cfg = 28 << 0 //
|
||||
ndmaSrcDrqUSB3 ndmaR8Cfg = 29 << 0 //
|
||||
ndmaSrcDrqUSB4 ndmaR8Cfg = 30 << 0 //
|
||||
ndmaSrcDrqUSB5 ndmaR8Cfg = 31 << 0 //
|
||||
)
|
||||
|
||||
// NDMA_CTRL_REG
|
||||
// R8: Page 129-131.
|
||||
type ndmaR8Cfg uint32
|
||||
|
||||
const (
|
||||
ddmaLoad ddmaR8Cfg = 1 << 31 // DDMA_LOAD
|
||||
ddmaBusy ddmaR8Cfg = 1 << 30 // DDMA_BSY_STA
|
||||
ddmaContinuous ddmaR8Cfg = 1 << 29 // DDMA_CONTI_MODE_EN
|
||||
// 28:27 reserved 28 = DDMA_CFG_DST_NON_SECURE ?
|
||||
ddmaDstWidth32 ddmaR8Cfg = 2 << 25 // DDMA_DST_DATA_WIDTH
|
||||
ddmaDstWidth16 ddmaR8Cfg = 1 << 25 //
|
||||
ddmaDstWidth8 ddmaR8Cfg = 0 << 25 //
|
||||
ddmaDstBurst8 ddmaR8Cfg = 2 << 23 // DDMA_DST_BST_LEN
|
||||
ddmaDstBurst4 ddmaR8Cfg = 1 << 23 //
|
||||
ddmaDstBurst1 ddmaR8Cfg = 0 << 23 //
|
||||
ddmaDstVertical ddmaR8Cfg = 3 << 21 // DDMA_ADDR_MODE; no idea what it's use it. It's not explained in the datasheet ...
|
||||
ddmaDstHorizontal ddmaR8Cfg = 2 << 21 // ... and the official drivers/dma/sun6i-dma.c driver doesn't use it
|
||||
ddmaDstIOMode ddmaR8Cfg = 1 << 21 // Non incrementing
|
||||
ddmaDstLinear ddmaR8Cfg = 0 << 21 // Normal incrementing position
|
||||
ddmaDstDrqSRAM ddmaR8Cfg = 0 << 16 // DDMA_DST_DRQ_SEL
|
||||
ddmaDstDrqSDRAM ddmaR8Cfg = 1 << 16 // DDR ram speed
|
||||
ddmaDstDrqNAND ddmaR8Cfg = 3 << 16 //
|
||||
ddmaDstDrqUSB0 ddmaR8Cfg = 4 << 16 //
|
||||
ddmaDstDrqSPI1TX ddmaR8Cfg = 8 << 16 //
|
||||
ddmaDstDrqCryptoTX ddmaR8Cfg = 10 << 16 //
|
||||
ddmaDstDrqTCON0 ddmaR8Cfg = 14 << 16 //
|
||||
ddmaDstDrqSPI0TX ddmaR8Cfg = 26 << 16 //
|
||||
ddmaDstDrqSPI2TX ddmaR8Cfg = 28 << 16 //
|
||||
ddmaBCRemain ddmaR8Cfg = 1 << 15 // BC_MODE_SEL
|
||||
// 14:11 reserved
|
||||
ddmaSrcWidth32 ddmaR8Cfg = 2 << 9 // DDMA_SRC_DATA_WIDTH
|
||||
ddmaSrcWidth16 ddmaR8Cfg = 1 << 9 //
|
||||
ddmaSrcWidth8 ddmaR8Cfg = 0 << 9 //
|
||||
ddmaSrcBurst8 ddmaR8Cfg = 2 << 7 // DDMA_SRC_BST_LEN
|
||||
ddmaSrcBurst4 ddmaR8Cfg = 1 << 7 //
|
||||
ddmaSrcBurst1 ddmaR8Cfg = 0 << 7 //
|
||||
ddmaSrcVertical ddmaR8Cfg = 3 << 5 // DDMA_SRC_ADDR_MODE
|
||||
ddmaSrcHorizontal ddmaR8Cfg = 2 << 5 //
|
||||
ddmaSrcIOMode ddmaR8Cfg = 1 << 5 // Non incrementing
|
||||
ddmaSrcLinear ddmaR8Cfg = 0 << 5 // Normal incrementing position
|
||||
// 4:0 drq
|
||||
ddmaSrcDrqSRAM ddmaR8Cfg = 0 << 0 // DDMA_SRC_DRQ_TYPE
|
||||
ddmaSrcDrqSDRAM ddmaR8Cfg = 1 << 0 //
|
||||
ddmaSrcDrqNAND ddmaR8Cfg = 3 << 0 //
|
||||
ddmaSrcDrqUSB0 ddmaR8Cfg = 4 << 0 //
|
||||
ddmaSrcDrqSPI1RX ddmaR8Cfg = 9 << 0 //
|
||||
ddmaSrcDrqCryptoRX ddmaR8Cfg = 11 << 0 //
|
||||
ddmaSrcDrqSPI0RX ddmaR8Cfg = 27 << 0 //
|
||||
ddmaSrcDrqSPI2RX ddmaR8Cfg = 29 << 0 //
|
||||
)
|
||||
|
||||
// DDMA_CFG_REG
|
||||
// R8: Page 131-134.
|
||||
type ddmaR8Cfg uint32
|
||||
|
||||
const (
|
||||
// For each value, N+1 is actually used.
|
||||
ddmaDstBlkSizeMask ddmaR8Param = 0xFF << 24 // DEST_DATA_BLK_SIZE
|
||||
ddmaDstWaitClkCycleMask ddmaR8Param = 0xFF << 16 // DEST_WAIT_CLK_CYC
|
||||
ddmaSrcBlkSizeMask ddmaR8Param = 0xFF << 8 // SRC_DATA_BLK_SIZE
|
||||
ddmaSrcWaitClkCycleMask ddmaR8Param = 0xFF << 0 // SRC_WAIT_CLK_CYC
|
||||
)
|
||||
|
||||
// DDMA_PARA_REG
|
||||
// R8: Page 134.
|
||||
type ddmaR8Param uint32
|
||||
|
||||
// smokeTest allocates two physical pages, ask the DMA controller to copy the
|
||||
// data from one page to another (with a small offset) and make sure the
|
||||
// content is as expected.
|
||||
//
|
||||
// This should take a fraction of a second and will make sure the driver is
|
||||
// usable.
|
||||
func smokeTest() error {
|
||||
const size = 4096 // 4kb
|
||||
const holeSize = 1 // Minimum DMA alignment.
|
||||
|
||||
alloc := func(s int) (pmem.Mem, error) {
|
||||
return pmem.Alloc(s)
|
||||
}
|
||||
|
||||
copyMem := func(pDst, pSrc uint64) error {
|
||||
n := drvDMA.dmaMemory.getDedicated()
|
||||
if n == -1 {
|
||||
return errors.New("no channel available")
|
||||
}
|
||||
drvDMA.dmaMemory.irqEn &^= 3 << uint(2*n+16)
|
||||
drvDMA.dmaMemory.irqPendStas = 3 << uint(2*n+16)
|
||||
ch := &drvDMA.dmaMemory.dedicated[n]
|
||||
defer func() {
|
||||
_ = ch.release()
|
||||
}()
|
||||
ch.set(uint32(pSrc), uint32(pDst)+holeSize, 4096-2*holeSize, false, false, ddmaDstDrqSDRAM|ddmaSrcDrqSDRAM)
|
||||
|
||||
for ch.cfg&ddmaBusy != 0 {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return pmem.TestCopy(size, holeSize, alloc, copyMem)
|
||||
}
|
||||
|
||||
// driverDMA implements periph.Driver.
|
||||
//
|
||||
// It implements much more than the DMA controller, it also exposes the clocks,
|
||||
// the PWM and PCM controllers.
|
||||
type driverDMA struct {
|
||||
// dmaMemory is the memory map of the CPU DMA registers.
|
||||
dmaMemory *dmaMap
|
||||
// pwmMemory is the memory map of the CPU PWM registers.
|
||||
pwmMemory *pwmMap
|
||||
// spiMemory is the memory mapping for the spi CPU registers.
|
||||
spiMemory *spiMap
|
||||
// clockMemory is the memory mapping for the clock CPU registers.
|
||||
clockMemory *clockMap
|
||||
// timerMemory is the memory mapping for the timer CPU registers.
|
||||
timerMemory *timerMap
|
||||
}
|
||||
|
||||
func (d *driverDMA) String() string {
|
||||
return "allwinner-dma"
|
||||
}
|
||||
|
||||
func (d *driverDMA) Prerequisites() []string {
|
||||
return []string{"allwinner-gpio"}
|
||||
}
|
||||
|
||||
func (d *driverDMA) After() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driverDMA) Init() (bool, error) {
|
||||
// dmaBaseAddr is the physical base address of the DMA registers.
|
||||
var dmaBaseAddr uint32
|
||||
// pwmBaseAddr is the physical base address of the PWM registers.
|
||||
var pwmBaseAddr uint32
|
||||
// spiBaseAddr is the physical base address of the clock registers.
|
||||
var spiBaseAddr uint32
|
||||
// clockBaseAddr is the physical base address of the clock registers.
|
||||
var clockBaseAddr uint32
|
||||
// timerBaseAddr is the physical base address of the timer registers.
|
||||
var timerBaseAddr uint32
|
||||
if IsA64() {
|
||||
// Page 198.
|
||||
dmaBaseAddr = 0x1C02000
|
||||
// Page 194.
|
||||
pwmBaseAddr = 0x1C21400
|
||||
// Page 161.
|
||||
timerBaseAddr = 0x1C20C00
|
||||
// Page 81.
|
||||
clockBaseAddr = 0x1C20000
|
||||
// Page Page 545.
|
||||
spiBaseAddr = 0x01C68000
|
||||
} else if IsR8() {
|
||||
// Page 124.
|
||||
dmaBaseAddr = 0x1C02000
|
||||
// Page 83.
|
||||
pwmBaseAddr = 0x1C20C00 + 0x200
|
||||
// Page 85.
|
||||
timerBaseAddr = 0x1C20C00
|
||||
// Page 57.
|
||||
clockBaseAddr = 0x1C20000
|
||||
// Page 151.
|
||||
spiBaseAddr = 0x01C05000
|
||||
} else {
|
||||
// H3
|
||||
// Page 194.
|
||||
//dmaBaseAddr = 0x1C02000
|
||||
// Page 187.
|
||||
//pwmBaseAddr = 0x1C21400
|
||||
// Page 154.
|
||||
//timerBaseAddr = 0x1C20C00
|
||||
return false, errors.New("unsupported CPU architecture")
|
||||
}
|
||||
|
||||
if err := pmem.MapAsPOD(uint64(dmaBaseAddr), &d.dmaMemory); err != nil {
|
||||
if os.IsPermission(err) {
|
||||
return true, fmt.Errorf("need more access, try as root: %v", err)
|
||||
}
|
||||
return true, err
|
||||
}
|
||||
|
||||
if err := pmem.MapAsPOD(uint64(pwmBaseAddr), &d.pwmMemory); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if err := pmem.MapAsPOD(uint64(timerBaseAddr), &d.timerMemory); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if err := pmem.MapAsPOD(uint64(clockBaseAddr), &d.clockMemory); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if err := pmem.MapAsPOD(uint64(spiBaseAddr), &d.spiMemory); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
return true, smokeTest()
|
||||
}
|
||||
|
||||
func (d *driverDMA) Close() error {
|
||||
// Stop DMA and PWM controllers.
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
if false && isArm {
|
||||
// TODO(maruel): This is intense, wait to be sure it works.
|
||||
driverreg.MustRegister(&drvDMA)
|
||||
}
|
||||
}
|
||||
|
||||
var drvDMA driverDMA
|
33
vendor/periph.io/x/host/v3/allwinner/doc.go
generated
vendored
Normal file
33
vendor/periph.io/x/host/v3/allwinner/doc.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package allwinner exposes the GPIO functionality that is common to all
|
||||
// AllWinner processors.
|
||||
//
|
||||
// This driver implements memory-mapped GPIO pin manipulation and leverages
|
||||
// sysfs-gpio for edge detection.
|
||||
//
|
||||
// If you are looking at the actual implementation, open doc.go for further
|
||||
// implementation details.
|
||||
//
|
||||
// Datasheets
|
||||
//
|
||||
// A64: http://files.pine64.org/doc/datasheet/pine64/Allwinner_A64_User_Manual_V1.0.pdf
|
||||
//
|
||||
// H3: http://dl.linux-sunxi.org/H3/Allwinner_H3_Datasheet_V1.0.pdf
|
||||
//
|
||||
// R8: https://github.com/NextThingCo/CHIP-Hardware/raw/master/CHIP%5Bv1_0%5D/CHIPv1_0-BOM-Datasheets/Allwinner%20R8%20User%20Manual%20V1.1.pdf
|
||||
//
|
||||
// Physical overview: http://files.pine64.org/doc/datasheet/pine64/A64_Datasheet_V1.1.pdf
|
||||
package allwinner
|
||||
|
||||
// Other implementation details
|
||||
//
|
||||
// The most active kernel branch is
|
||||
// https://github.com/linux-sunxi/linux-sunxi/commits/sunxi-next
|
||||
//
|
||||
// In particular look at
|
||||
// https://github.com/linux-sunxi/linux-sunxi/blob/sunxi-next/drivers/dma/sun4i-dma.c
|
||||
// and
|
||||
// https://github.com/linux-sunxi/linux-sunxi/blob/sunxi-next/drivers/dma/sun6i-dma.c
|
1065
vendor/periph.io/x/host/v3/allwinner/gpio.go
generated
vendored
Normal file
1065
vendor/periph.io/x/host/v3/allwinner/gpio.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
563
vendor/periph.io/x/host/v3/allwinner/gpio_pl.go
generated
vendored
Normal file
563
vendor/periph.io/x/host/v3/allwinner/gpio_pl.go
generated
vendored
Normal file
@ -0,0 +1,563 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package allwinner
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"periph.io/x/conn/v3/driver/driverreg"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/gpio/gpioreg"
|
||||
"periph.io/x/conn/v3/physic"
|
||||
"periph.io/x/conn/v3/pin"
|
||||
"periph.io/x/host/v3/pmem"
|
||||
"periph.io/x/host/v3/sysfs"
|
||||
)
|
||||
|
||||
// All the pins in the PL group.
|
||||
var PL0, PL1, PL2, PL3, PL4, PL5, PL6, PL7, PL8, PL9, PL10, PL11, PL12 *PinPL
|
||||
|
||||
// PinPL defines one CPU supported pin in the PL group.
|
||||
//
|
||||
// PinPL implements gpio.PinIO.
|
||||
type PinPL struct {
|
||||
// Immutable.
|
||||
offset uint8 // as per register offset calculation
|
||||
name string // name as per datasheet
|
||||
defaultPull gpio.Pull // default pull at startup
|
||||
|
||||
// Immutable after driver initialization.
|
||||
sysfsPin *sysfs.Pin // Set to the corresponding sysfs.Pin, if any.
|
||||
available bool // Set when the pin is available on this CPU architecture.
|
||||
|
||||
// Mutable.
|
||||
usingEdge bool // Set when edge detection is enabled.
|
||||
}
|
||||
|
||||
// String implements conn.Resource.
|
||||
//
|
||||
// It returns the pin name and number, ex: "PL5(352)".
|
||||
func (p *PinPL) String() string {
|
||||
return fmt.Sprintf("%s(%d)", p.name, p.Number())
|
||||
}
|
||||
|
||||
// Halt implements conn.Resource.
|
||||
//
|
||||
// It stops edge detection if enabled.
|
||||
func (p *PinPL) Halt() error {
|
||||
if p.usingEdge {
|
||||
if err := p.sysfsPin.Halt(); err != nil {
|
||||
return p.wrap(err)
|
||||
}
|
||||
p.usingEdge = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name implements pin.Pin.
|
||||
//
|
||||
// It returns the pin name, ex: "PL5".
|
||||
func (p *PinPL) Name() string {
|
||||
return p.name
|
||||
}
|
||||
|
||||
// Number implements pin.Pin.
|
||||
//
|
||||
// It returns the GPIO pin number as represented by gpio sysfs.
|
||||
func (p *PinPL) Number() int {
|
||||
return 11*32 + int(p.offset)
|
||||
}
|
||||
|
||||
// Function implements pin.Pin.
|
||||
func (p *PinPL) Function() string {
|
||||
return string(p.Func())
|
||||
}
|
||||
|
||||
// Func implements pin.PinFunc.
|
||||
func (p *PinPL) Func() pin.Func {
|
||||
if !p.available {
|
||||
// We do not want the error message about uninitialized system.
|
||||
return pin.FuncNone
|
||||
}
|
||||
if drvGPIOPL.gpioMemoryPL == nil {
|
||||
if p.sysfsPin == nil {
|
||||
return pin.FuncNone
|
||||
}
|
||||
return p.sysfsPin.Func()
|
||||
}
|
||||
switch f := p.function(); f {
|
||||
case in:
|
||||
if p.FastRead() {
|
||||
return gpio.IN_HIGH
|
||||
}
|
||||
return gpio.IN_LOW
|
||||
case out:
|
||||
if p.FastRead() {
|
||||
return gpio.OUT_HIGH
|
||||
}
|
||||
return gpio.OUT_LOW
|
||||
case alt1:
|
||||
if s := mappingPL[p.offset][0]; len(s) != 0 {
|
||||
return pin.Func(s)
|
||||
}
|
||||
return pin.Func("ALT1")
|
||||
case alt2:
|
||||
if s := mappingPL[p.offset][1]; len(s) != 0 {
|
||||
return pin.Func(s)
|
||||
}
|
||||
return pin.Func("ALT2")
|
||||
case alt3:
|
||||
if s := mappingPL[p.offset][2]; len(s) != 0 {
|
||||
return pin.Func(s)
|
||||
}
|
||||
return pin.Func("ALT3")
|
||||
case alt4:
|
||||
if s := mappingPL[p.offset][3]; len(s) != 0 {
|
||||
return pin.Func(s)
|
||||
}
|
||||
return pin.Func("ALT4")
|
||||
case alt5:
|
||||
if s := mappingPL[p.offset][4]; len(s) != 0 {
|
||||
if strings.Contains(string(s), "_EINT") {
|
||||
// It's an input supporting interrupts.
|
||||
if p.FastRead() {
|
||||
return gpio.IN_HIGH
|
||||
}
|
||||
return gpio.IN_LOW
|
||||
}
|
||||
return pin.Func(s)
|
||||
}
|
||||
return pin.Func("ALT5")
|
||||
case disabled:
|
||||
return pin.FuncNone
|
||||
default:
|
||||
return pin.FuncNone
|
||||
}
|
||||
}
|
||||
|
||||
// SupportedFuncs implements pin.PinFunc.
|
||||
func (p *PinPL) SupportedFuncs() []pin.Func {
|
||||
f := make([]pin.Func, 0, 2+2)
|
||||
f = append(f, gpio.IN, gpio.OUT)
|
||||
for _, m := range mappingPL[p.offset] {
|
||||
if m != pin.FuncNone && !strings.Contains(string(m), "_EINT") {
|
||||
f = append(f, m)
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// SetFunc implements pin.PinFunc.
|
||||
func (p *PinPL) SetFunc(f pin.Func) error {
|
||||
switch f {
|
||||
case gpio.FLOAT:
|
||||
return p.In(gpio.Float, gpio.NoEdge)
|
||||
case gpio.IN:
|
||||
return p.In(gpio.PullNoChange, gpio.NoEdge)
|
||||
case gpio.IN_LOW:
|
||||
return p.In(gpio.PullDown, gpio.NoEdge)
|
||||
case gpio.IN_HIGH:
|
||||
return p.In(gpio.PullUp, gpio.NoEdge)
|
||||
case gpio.OUT_HIGH:
|
||||
return p.Out(gpio.High)
|
||||
case gpio.OUT_LOW:
|
||||
return p.Out(gpio.Low)
|
||||
default:
|
||||
isGeneral := f == f.Generalize()
|
||||
for i, m := range mappingPL[p.offset] {
|
||||
if m == f || (isGeneral && m.Generalize() == f) {
|
||||
if err := p.Halt(); err != nil {
|
||||
return err
|
||||
}
|
||||
switch i {
|
||||
case 0:
|
||||
p.setFunction(alt1)
|
||||
case 1:
|
||||
p.setFunction(alt2)
|
||||
case 2:
|
||||
p.setFunction(alt3)
|
||||
case 3:
|
||||
p.setFunction(alt4)
|
||||
case 4:
|
||||
p.setFunction(alt5)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return p.wrap(errors.New("unsupported function"))
|
||||
}
|
||||
}
|
||||
|
||||
// In implements gpio.PinIn.
|
||||
func (p *PinPL) In(pull gpio.Pull, edge gpio.Edge) error {
|
||||
if !p.available {
|
||||
// We do not want the error message about uninitialized system.
|
||||
return p.wrap(errors.New("not available on this CPU architecture"))
|
||||
}
|
||||
if p.usingEdge && edge == gpio.NoEdge {
|
||||
if err := p.sysfsPin.Halt(); err != nil {
|
||||
return p.wrap(err)
|
||||
}
|
||||
p.usingEdge = false
|
||||
}
|
||||
if drvGPIOPL.gpioMemoryPL == nil {
|
||||
if p.sysfsPin == nil {
|
||||
return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible; try running as root?"))
|
||||
}
|
||||
if pull != gpio.PullNoChange {
|
||||
return p.wrap(errors.New("pull cannot be used when subsystem gpiomem not initialized; try running as root?"))
|
||||
}
|
||||
if err := p.sysfsPin.In(pull, edge); err != nil {
|
||||
return p.wrap(err)
|
||||
}
|
||||
p.usingEdge = edge != gpio.NoEdge
|
||||
return nil
|
||||
}
|
||||
if !p.setFunction(in) {
|
||||
return p.wrap(errors.New("failed to set pin as input"))
|
||||
}
|
||||
if pull != gpio.PullNoChange {
|
||||
off := p.offset / 16
|
||||
shift := 2 * (p.offset % 16)
|
||||
// Do it in a way that is concurrent safe.
|
||||
drvGPIOPL.gpioMemoryPL.pull[off] &^= 3 << shift
|
||||
switch pull {
|
||||
case gpio.PullDown:
|
||||
drvGPIOPL.gpioMemoryPL.pull[off] = 2 << shift
|
||||
case gpio.PullUp:
|
||||
drvGPIOPL.gpioMemoryPL.pull[off] = 1 << shift
|
||||
default:
|
||||
}
|
||||
}
|
||||
if edge != gpio.NoEdge {
|
||||
if p.sysfsPin == nil {
|
||||
return p.wrap(fmt.Errorf("pin %d is not exported by sysfs", p.Number()))
|
||||
}
|
||||
// This resets pending edges.
|
||||
if err := p.sysfsPin.In(gpio.PullNoChange, edge); err != nil {
|
||||
return p.wrap(err)
|
||||
}
|
||||
p.usingEdge = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read implements gpio.PinIn.
|
||||
func (p *PinPL) Read() gpio.Level {
|
||||
if drvGPIOPL.gpioMemoryPL == nil {
|
||||
if p.sysfsPin == nil {
|
||||
return gpio.Low
|
||||
}
|
||||
return p.sysfsPin.Read()
|
||||
}
|
||||
return gpio.Level(drvGPIOPL.gpioMemoryPL.data&(1<<p.offset) != 0)
|
||||
}
|
||||
|
||||
// FastRead reads without verification.
|
||||
func (p *PinPL) FastRead() gpio.Level {
|
||||
return gpio.Level(drvGPIOPL.gpioMemoryPL.data&(1<<p.offset) != 0)
|
||||
}
|
||||
|
||||
// WaitForEdge implements gpio.PinIn.
|
||||
func (p *PinPL) WaitForEdge(timeout time.Duration) bool {
|
||||
if p.sysfsPin != nil {
|
||||
return p.sysfsPin.WaitForEdge(timeout)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Pull implements gpio.PinIn.
|
||||
func (p *PinPL) Pull() gpio.Pull {
|
||||
if drvGPIOPL.gpioMemoryPL == nil {
|
||||
// If gpioMemoryPL is set, p.available is true.
|
||||
return gpio.PullNoChange
|
||||
}
|
||||
switch (drvGPIOPL.gpioMemoryPL.pull[p.offset/16] >> (2 * (p.offset % 16))) & 3 {
|
||||
case 0:
|
||||
return gpio.Float
|
||||
case 1:
|
||||
return gpio.PullUp
|
||||
case 2:
|
||||
return gpio.PullDown
|
||||
default:
|
||||
// Confused.
|
||||
return gpio.PullNoChange
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultPull implements gpio.PinIn.
|
||||
func (p *PinPL) DefaultPull() gpio.Pull {
|
||||
return p.defaultPull
|
||||
}
|
||||
|
||||
// Out implements gpio.PinOut.
|
||||
func (p *PinPL) Out(l gpio.Level) error {
|
||||
if !p.available {
|
||||
// We do not want the error message about uninitialized system.
|
||||
return p.wrap(errors.New("not available on this CPU architecture"))
|
||||
}
|
||||
if drvGPIOPL.gpioMemoryPL == nil {
|
||||
if p.sysfsPin != nil {
|
||||
return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible; try running as root?"))
|
||||
}
|
||||
return p.sysfsPin.Out(l)
|
||||
}
|
||||
// First disable edges.
|
||||
if err := p.Halt(); err != nil {
|
||||
return err
|
||||
}
|
||||
p.FastOut(l)
|
||||
if !p.setFunction(out) {
|
||||
return p.wrap(errors.New("failed to set pin as output"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FastOut sets a pin output level with Absolutely No error checking.
|
||||
//
|
||||
// See Pin.FastOut for more information.
|
||||
func (p *PinPL) FastOut(l gpio.Level) {
|
||||
bit := uint32(1 << p.offset)
|
||||
if l {
|
||||
drvGPIOPL.gpioMemoryPL.data |= bit
|
||||
} else {
|
||||
drvGPIOPL.gpioMemoryPL.data &^= bit
|
||||
}
|
||||
}
|
||||
|
||||
// PWM implements gpio.PinOut.
|
||||
func (p *PinPL) PWM(gpio.Duty, physic.Frequency) error {
|
||||
// TODO(maruel): PWM support for PL10.
|
||||
return p.wrap(errors.New("not available on this CPU architecture"))
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// function returns the current GPIO pin function.
|
||||
//
|
||||
// It must not be called if drvGPIOPL.gpioMemoryPL is nil.
|
||||
func (p *PinPL) function() function {
|
||||
shift := 4 * (p.offset % 8)
|
||||
return function((drvGPIOPL.gpioMemoryPL.cfg[p.offset/8] >> shift) & 7)
|
||||
}
|
||||
|
||||
// setFunction changes the GPIO pin function.
|
||||
//
|
||||
// Returns false if the pin was in AltN. Only accepts in and out
|
||||
//
|
||||
// It must not be called if drvGPIOPL.gpioMemoryPL is nil.
|
||||
func (p *PinPL) setFunction(f function) bool {
|
||||
if f != in && f != out {
|
||||
return false
|
||||
}
|
||||
// Interrupt based edge triggering is Alt5 but this is only supported on some
|
||||
// pins.
|
||||
// TODO(maruel): This check should use a whitelist of pins.
|
||||
if actual := p.function(); actual != in && actual != out && actual != disabled && actual != alt5 {
|
||||
// Pin is in special mode.
|
||||
return false
|
||||
}
|
||||
off := p.offset / 8
|
||||
shift := 4 * (p.offset % 8)
|
||||
mask := uint32(disabled) << shift
|
||||
v := (uint32(f) << shift) ^ mask
|
||||
// First disable, then setup. This is concurrent safe.
|
||||
drvGPIOPL.gpioMemoryPL.cfg[off] |= mask
|
||||
drvGPIOPL.gpioMemoryPL.cfg[off] &^= v
|
||||
if p.function() != f {
|
||||
panic(f)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *PinPL) wrap(err error) error {
|
||||
return fmt.Errorf("allwinner-gpio-pl (%s): %v", p, err)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// cpuPinsPL is all the pins as supported by the CPU. There is no guarantee that
|
||||
// they are actually connected to anything on the board.
|
||||
var cpuPinsPL = []PinPL{
|
||||
{offset: 0, name: "PL0", defaultPull: gpio.PullUp},
|
||||
{offset: 1, name: "PL1", defaultPull: gpio.PullUp},
|
||||
{offset: 2, name: "PL2", defaultPull: gpio.Float},
|
||||
{offset: 3, name: "PL3", defaultPull: gpio.Float},
|
||||
{offset: 4, name: "PL4", defaultPull: gpio.Float},
|
||||
{offset: 5, name: "PL5", defaultPull: gpio.Float},
|
||||
{offset: 6, name: "PL6", defaultPull: gpio.Float},
|
||||
{offset: 7, name: "PL7", defaultPull: gpio.Float},
|
||||
{offset: 8, name: "PL8", defaultPull: gpio.Float},
|
||||
{offset: 9, name: "PL9", defaultPull: gpio.Float},
|
||||
{offset: 10, name: "PL10", defaultPull: gpio.Float},
|
||||
{offset: 11, name: "PL11", defaultPull: gpio.Float},
|
||||
{offset: 12, name: "PL12", defaultPull: gpio.Float},
|
||||
}
|
||||
|
||||
// See gpio.go for details.
|
||||
var mappingPL = [13][5]pin.Func{
|
||||
{"RSB_SCK", "I2C_SCL", "", "", "PL_EINT0"}, // PL0
|
||||
{"RSB_SDA", "I2C_SDA", "", "", "PL_EINT1"}, // PL1
|
||||
{"UART_TX", "", "", "", "PL_EINT2"}, // PL2
|
||||
{"UART_RX", "", "", "", "PL_EINT3"}, // PL3
|
||||
{"JTAG_TMS", "", "", "", "PL_EINT4"}, // PL4
|
||||
{"JTAG_TCK", "", "", "", "PL_EINT5"}, // PL5
|
||||
{"JTAG_TDO", "", "", "", "PL_EINT6"}, // PL6
|
||||
{"JTAG_TDI", "", "", "", "PL_EINT7"}, // PL7
|
||||
{"I2C_SCL", "", "", "", "PL_EINT8"}, // PL8
|
||||
{"I2C_SDA", "", "", "", "PL_EINT9"}, // PL9
|
||||
{"PWM0", "", "", "", "PL_EINT10"}, // PL10
|
||||
{"CIR_RX", "", "", "", "PL_EINT11"}, // PL11
|
||||
{"", "", "", "", "PL_EINT12"}, // PL12
|
||||
}
|
||||
|
||||
func init() {
|
||||
PL0 = &cpuPinsPL[0]
|
||||
PL1 = &cpuPinsPL[1]
|
||||
PL2 = &cpuPinsPL[2]
|
||||
PL3 = &cpuPinsPL[3]
|
||||
PL4 = &cpuPinsPL[4]
|
||||
PL5 = &cpuPinsPL[5]
|
||||
PL6 = &cpuPinsPL[6]
|
||||
PL7 = &cpuPinsPL[7]
|
||||
PL8 = &cpuPinsPL[8]
|
||||
PL9 = &cpuPinsPL[9]
|
||||
PL10 = &cpuPinsPL[10]
|
||||
PL11 = &cpuPinsPL[11]
|
||||
PL12 = &cpuPinsPL[12]
|
||||
}
|
||||
|
||||
// getBaseAddressPL queries the virtual file system to retrieve the base address
|
||||
// of the GPIO registers for GPIO pins in group PL.
|
||||
//
|
||||
// Defaults to 0x01F02C00 as per datasheet if could query the file system.
|
||||
func getBaseAddressPL() uint64 {
|
||||
base := uint64(0x01F02C00)
|
||||
link, err := os.Readlink("/sys/bus/platform/drivers/sun50i-r-pinctrl/driver")
|
||||
if err != nil {
|
||||
return base
|
||||
}
|
||||
parts := strings.SplitN(path.Base(link), ".", 2)
|
||||
if len(parts) != 2 {
|
||||
return base
|
||||
}
|
||||
base2, err := strconv.ParseUint(parts[0], 16, 64)
|
||||
if err != nil {
|
||||
return base
|
||||
}
|
||||
return base2
|
||||
}
|
||||
|
||||
// driverGPIOPL implements periph.Driver.
|
||||
type driverGPIOPL struct {
|
||||
// gpioMemoryPL is only the PL group in that case. Note that groups PI, PJ, PK
|
||||
// do not exist.
|
||||
gpioMemoryPL *gpioGroup
|
||||
}
|
||||
|
||||
func (d *driverGPIOPL) String() string {
|
||||
return "allwinner-gpio-pl"
|
||||
}
|
||||
|
||||
func (d *driverGPIOPL) Prerequisites() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driverGPIOPL) After() []string {
|
||||
return []string{"sysfs-gpio"}
|
||||
}
|
||||
|
||||
func (d *driverGPIOPL) Init() (bool, error) {
|
||||
// BUG(maruel): H3 supports group PL too.
|
||||
if !IsA64() {
|
||||
return false, errors.New("no A64 CPU detected")
|
||||
}
|
||||
|
||||
// Mark the right pins as available even if the memory map fails so they can
|
||||
// callback to sysfs.Pins.
|
||||
functions := map[pin.Func]struct{}{}
|
||||
for i := range cpuPinsPL {
|
||||
name := cpuPinsPL[i].Name()
|
||||
num := strconv.Itoa(cpuPinsPL[i].Number())
|
||||
cpuPinsPL[i].available = true
|
||||
gpion := "GPIO" + num
|
||||
|
||||
// Unregister the pin if already registered. This happens with sysfs-gpio.
|
||||
// Do not error on it, since sysfs-gpio may have failed to load.
|
||||
_ = gpioreg.Unregister(gpion)
|
||||
_ = gpioreg.Unregister(num)
|
||||
|
||||
// Register the pin with gpio.
|
||||
if err := gpioreg.Register(&cpuPinsPL[i]); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if err := gpioreg.RegisterAlias(gpion, name); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if err := gpioreg.RegisterAlias(num, name); err != nil {
|
||||
return true, err
|
||||
}
|
||||
switch f := cpuPinsPL[i].Func(); f {
|
||||
case gpio.IN, gpio.OUT, pin.FuncNone:
|
||||
default:
|
||||
// Registering the same alias twice fails. This can happen if two pins
|
||||
// are configured with the same function.
|
||||
if _, ok := functions[f]; !ok {
|
||||
// TODO(maruel): We'd have to clear out the ones from allwinner-gpio
|
||||
// too.
|
||||
functions[f] = struct{}{}
|
||||
_ = gpioreg.RegisterAlias(string(f), name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now do a second loop but do the alternate functions.
|
||||
for i := range cpuPinsPL {
|
||||
for _, f := range cpuPinsPL[i].SupportedFuncs() {
|
||||
switch f {
|
||||
case gpio.IN, gpio.OUT:
|
||||
default:
|
||||
if _, ok := functions[f]; !ok {
|
||||
// TODO(maruel): We'd have to clear out the ones from allwinner-gpio
|
||||
// too.
|
||||
functions[f] = struct{}{}
|
||||
_ = gpioreg.RegisterAlias(string(f), cpuPinsPL[i].name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m, err := pmem.Map(getBaseAddressPL(), 4096)
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
return true, fmt.Errorf("need more access, try as root: %v", err)
|
||||
}
|
||||
return true, err
|
||||
}
|
||||
if err := m.AsPOD(&d.gpioMemoryPL); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
if isArm {
|
||||
driverreg.MustRegister(&drvGPIOPL)
|
||||
}
|
||||
}
|
||||
|
||||
var drvGPIOPL driverGPIOPL
|
||||
|
||||
var _ gpio.PinIO = &PinPL{}
|
||||
var _ gpio.PinIn = &PinPL{}
|
||||
var _ gpio.PinOut = &PinPL{}
|
||||
var _ pin.PinFunc = &PinPL{}
|
197
vendor/periph.io/x/host/v3/allwinner/pwm.go
generated
vendored
Normal file
197
vendor/periph.io/x/host/v3/allwinner/pwm.go
generated
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package allwinner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const pwmClock = 24000000
|
||||
const pwmMaxPeriod = 0x10000
|
||||
|
||||
// prescalers is the value for pwm0Prescale*
|
||||
var prescalers = []struct {
|
||||
freq uint32
|
||||
scaler pwmPrescale
|
||||
}{
|
||||
// Base frequency (min freq is half that) / PWM clock at pwmMaxPeriod
|
||||
{pwmClock, pwmPrescale1}, // 24MHz / 366Hz
|
||||
{pwmClock / 120, pwmPrescale120}, // 200kHz / 3Hz
|
||||
{pwmClock / 180, pwmPrescale180}, // 133kHz / 2Hz
|
||||
{pwmClock / 240, pwmPrescale240}, // 100kHz / 1.5Hz
|
||||
{pwmClock / 360, pwmPrescale360}, // 66kHz / 1.01Hz
|
||||
{pwmClock / 480, pwmPrescale480}, // 50kHz / 0.7Hz
|
||||
{pwmClock / 12000, pwmPrescale12000}, // 2kHz
|
||||
{pwmClock / 24000, pwmPrescale24000}, // 1kHz
|
||||
{pwmClock / 36000, pwmPrescale36000}, // 666 Hz
|
||||
{pwmClock / 48000, pwmPrescale48000}, // 500 Hz
|
||||
{pwmClock / 72000, pwmPrescale72000}, // 333 Hz / 0.005Hz
|
||||
}
|
||||
|
||||
const (
|
||||
// 31:29 reserved
|
||||
pwmBusy pwmCtl = 1 << 28 // PWM0_RDY
|
||||
// 27:10 reserved (used for pwm1)
|
||||
pwm0Mask pwmCtl = (1 << 10) - 1
|
||||
pwm0Bypass pwmCtl = 1 << 9 // PWM0_BYPASS (marked as unused on some drivers?)
|
||||
pwm0PulseStart pwmCtl = 1 << 8 // PWM_CH0_PUL_START
|
||||
pwm0ModePulse pwmCtl = 1 << 7 // PWM_CHANNEL0_MODE
|
||||
pwm0SCLK pwmCtl = 1 << 6 // SCLK_CH0_GATING
|
||||
pwm0Polarity pwmCtl = 1 << 5 // PWM_CH0_ACT_STA
|
||||
pwm0Enable pwmCtl = 1 << 4 // PWM_CH0_EN
|
||||
// 3:0
|
||||
pwm0PrescaleMask pwmCtl = pwmCtl(pwmPrescaleMask) // PWM_CH0_PRESCAL
|
||||
pwm0Prescale120 pwmCtl = pwmCtl(pwmPrescale120)
|
||||
pwm0Prescale180 pwmCtl = pwmCtl(pwmPrescale180)
|
||||
pwm0Prescale240 pwmCtl = pwmCtl(pwmPrescale240)
|
||||
pwm0Prescale360 pwmCtl = pwmCtl(pwmPrescale360)
|
||||
pwm0Prescale480 pwmCtl = pwmCtl(pwmPrescale480)
|
||||
// 5, 6, 7 reserved
|
||||
pwm0Prescale12000 pwmCtl = pwmCtl(pwmPrescale12000)
|
||||
pwm0Prescale24000 pwmCtl = pwmCtl(pwmPrescale24000)
|
||||
pwm0Prescale36000 pwmCtl = pwmCtl(pwmPrescale36000)
|
||||
pwm0Prescale48000 pwmCtl = pwmCtl(pwmPrescale48000)
|
||||
pwm0Prescale72000 pwmCtl = pwmCtl(pwmPrescale72000)
|
||||
// 13, 14 reserved
|
||||
pwm0Prescale1 pwmCtl = pwmCtl(pwmPrescale1)
|
||||
)
|
||||
|
||||
// A64: Pages 194-195.
|
||||
// R8: Pages 83-84.
|
||||
type pwmCtl uint32
|
||||
|
||||
func (p pwmCtl) String() string {
|
||||
var out []string
|
||||
if p&pwmBusy != 0 {
|
||||
out = append(out, "PWM0_RDY")
|
||||
p &^= pwmBusy
|
||||
}
|
||||
if p&pwm0Bypass != 0 {
|
||||
out = append(out, "PWM0_BYPASS")
|
||||
p &^= pwm0Bypass
|
||||
}
|
||||
if p&pwm0PulseStart != 0 {
|
||||
out = append(out, "PWM0_CH0_PUL_START")
|
||||
p &^= pwm0PulseStart
|
||||
}
|
||||
if p&pwm0ModePulse != 0 {
|
||||
out = append(out, "PWM0_CHANNEL0_MODE")
|
||||
p &^= pwm0ModePulse
|
||||
}
|
||||
if p&pwm0SCLK != 0 {
|
||||
out = append(out, "SCLK_CH0_GATING")
|
||||
p &^= pwm0SCLK
|
||||
}
|
||||
if p&pwm0Polarity != 0 {
|
||||
out = append(out, "PWM_CH0_ACT_STA")
|
||||
p &^= pwm0Polarity
|
||||
}
|
||||
if p&pwm0Enable != 0 {
|
||||
out = append(out, "PWM_CH0_EN")
|
||||
p &^= pwm0Enable
|
||||
}
|
||||
out = append(out, pwmPrescale(p&pwm0PrescaleMask).String())
|
||||
p &^= pwm0PrescaleMask
|
||||
if p != 0 {
|
||||
out = append(out, fmt.Sprintf("Unknown(0x%08X)", uint32(p)))
|
||||
}
|
||||
return strings.Join(out, "|")
|
||||
}
|
||||
|
||||
const (
|
||||
pwmPrescaleMask pwmPrescale = 0xF
|
||||
pwmPrescale120 pwmPrescale = 0
|
||||
pwmPrescale180 pwmPrescale = 1
|
||||
pwmPrescale240 pwmPrescale = 2
|
||||
pwmPrescale360 pwmPrescale = 3
|
||||
pwmPrescale480 pwmPrescale = 4
|
||||
// 5, 6, 7 reserved
|
||||
pwmPrescale12000 pwmPrescale = 8
|
||||
pwmPrescale24000 pwmPrescale = 9
|
||||
pwmPrescale36000 pwmPrescale = 10
|
||||
pwmPrescale48000 pwmPrescale = 11
|
||||
pwmPrescale72000 pwmPrescale = 12
|
||||
// 13, 14 reserved
|
||||
pwmPrescale1 pwmPrescale = 15
|
||||
)
|
||||
|
||||
type pwmPrescale uint32
|
||||
|
||||
func (p pwmPrescale) String() string {
|
||||
switch p {
|
||||
case pwmPrescale120:
|
||||
return "/120"
|
||||
case pwmPrescale180:
|
||||
return "/180"
|
||||
case pwmPrescale240:
|
||||
return "/240"
|
||||
case pwmPrescale360:
|
||||
return "/360"
|
||||
case pwmPrescale480:
|
||||
return "/480"
|
||||
case pwmPrescale12000:
|
||||
return "/12k"
|
||||
case pwmPrescale24000:
|
||||
return "/24k"
|
||||
case pwmPrescale36000:
|
||||
return "/36k"
|
||||
case pwmPrescale48000:
|
||||
return "/48k"
|
||||
case pwmPrescale72000:
|
||||
return "/72k"
|
||||
case pwmPrescale1:
|
||||
return "/1"
|
||||
default:
|
||||
return fmt.Sprintf("InvalidScalar(%d)", p&pwmPrescaleMask)
|
||||
}
|
||||
}
|
||||
|
||||
// A64: Page 195.
|
||||
// R8: Page 84
|
||||
type pwmPeriod uint32
|
||||
|
||||
func (p pwmPeriod) String() string {
|
||||
return fmt.Sprintf("%d/%d", p&0xFFFF, uint32((p>>16)&0xFFFF)+1)
|
||||
}
|
||||
|
||||
func toPeriod(total uint32, active uint16) pwmPeriod {
|
||||
if total > pwmMaxPeriod {
|
||||
total = pwmMaxPeriod
|
||||
}
|
||||
return pwmPeriod(total-1)<<16 | pwmPeriod(active)
|
||||
}
|
||||
|
||||
// getBestPrescale finds the best prescaler.
|
||||
//
|
||||
// Cycles must be between 2 and 0x10000/2.
|
||||
func getBestPrescale(period time.Duration) pwmPrescale {
|
||||
// TODO(maruel): Rewrite this function, it is incorrect.
|
||||
for _, v := range prescalers {
|
||||
p := time.Second / time.Duration(v.freq)
|
||||
smallest := (period / pwmMaxPeriod)
|
||||
largest := (period / 2)
|
||||
if p > smallest && p < largest {
|
||||
return v.scaler
|
||||
}
|
||||
}
|
||||
// Period is longer than 196s.
|
||||
return pwmPrescale72000
|
||||
}
|
||||
|
||||
// pwmMap represents the PWM memory mapped CPU registers.
|
||||
//
|
||||
// The base frequency is 24Mhz.
|
||||
//
|
||||
// TODO(maruel): Some CPU have 2 PWMs.
|
||||
type pwmMap struct {
|
||||
ctl pwmCtl // PWM_CTRL_REG
|
||||
period pwmPeriod // PWM_CH0_PERIOD
|
||||
}
|
||||
|
||||
func (p *pwmMap) String() string {
|
||||
return fmt.Sprintf("pwmMap{%s, %v}", p.ctl, p.period)
|
||||
}
|
149
vendor/periph.io/x/host/v3/allwinner/r8.go
generated
vendored
Normal file
149
vendor/periph.io/x/host/v3/allwinner/r8.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// This file contains pin mapping information that is specific to the Allwinner
|
||||
// R8 model.
|
||||
|
||||
package allwinner
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"periph.io/x/conn/v3/pin"
|
||||
"periph.io/x/host/v3/sysfs"
|
||||
)
|
||||
|
||||
// R8 specific pins.
|
||||
var (
|
||||
FEL *pin.BasicPin // Boot mode selection
|
||||
MIC_IN *pin.BasicPin // Microphone in
|
||||
MIC_GND *pin.BasicPin // Microphone ground
|
||||
HP_LEFT *pin.BasicPin // Left speaker out
|
||||
HP_RIGHT *pin.BasicPin // Right speaker out
|
||||
HP_COM *pin.BasicPin // Speaker common
|
||||
X1, X2, Y1, Y2 *pin.BasicPin // Touch screen pins
|
||||
)
|
||||
|
||||
//
|
||||
|
||||
func init() {
|
||||
FEL = &pin.BasicPin{N: "FEL"}
|
||||
MIC_IN = &pin.BasicPin{N: "MIC_IN"}
|
||||
MIC_GND = &pin.BasicPin{N: "MIC_GND"}
|
||||
HP_LEFT = &pin.BasicPin{N: "HP_LEFT"}
|
||||
HP_RIGHT = &pin.BasicPin{N: "HP_RIGHT"}
|
||||
HP_COM = &pin.BasicPin{N: "HP_COM"}
|
||||
|
||||
X1 = &pin.BasicPin{N: "X1"}
|
||||
X2 = &pin.BasicPin{N: "X2"}
|
||||
Y1 = &pin.BasicPin{N: "Y1"}
|
||||
Y2 = &pin.BasicPin{N: "Y2"}
|
||||
}
|
||||
|
||||
// mappingR8 describes the mapping of each R8 processor gpio to their alternate
|
||||
// functions.
|
||||
//
|
||||
// It omits the in & out functions which are available on all pins.
|
||||
//
|
||||
// The mapping comes from the datasheet page 18:
|
||||
// https://github.com/NextThingCo/CHIP-Hardware/raw/master/CHIP%5Bv1_0%5D/CHIPv1_0-BOM-Datasheets/Allwinner%20R8%20Datasheet%20V1.2.pdf
|
||||
//
|
||||
// - The datasheet uses TWI instead of I2C but this is renamed here for consistency.
|
||||
var mappingR8 = map[string][5]pin.Func{
|
||||
"PB0": {"I2C0_SCL"},
|
||||
"PB1": {"I2C0_SDA"},
|
||||
"PB2": {"PWM0", "", "", "", "EINT16"},
|
||||
"PB3": {"IR_TX", "", "", "", "EINT17"},
|
||||
"PB4": {"IR_RX", "", "", "", "EINT18"},
|
||||
"PB10": {"SPI2_CS1"},
|
||||
"PB15": {"I2C1_SCL"},
|
||||
"PB16": {"I2C1_SDA"},
|
||||
"PB17": {"I2C2_SCL"},
|
||||
"PB18": {"I2C2_SDA"},
|
||||
"PC0": {"NAND_WE", "SPI0_MOSI"},
|
||||
"PC1": {"NAND_ALE", "SPI0_MISO"},
|
||||
"PC2": {"NAND_CLE", "SPI0_CLK"},
|
||||
"PC3": {"NAND_CE1", "SPI0_CS0"},
|
||||
"PC4": {"NAND_CE0"},
|
||||
"PC5": {"NAND_RE"},
|
||||
"PC6": {"NAND_RB0", "SDC2_CMD"},
|
||||
"PC7": {"NAND_RB1", "SDC2_CLK"},
|
||||
"PC8": {"NAND_DQ0", "SDC2_D0"},
|
||||
"PC9": {"NAND_DQ1", "SDC2_D1"},
|
||||
"PC10": {"NAND_DQ2", "SDC2_D2"},
|
||||
"PC11": {"NAND_DQ3", "SDC2_D3"},
|
||||
"PC12": {"NAND_DQ4", "SDC2_D4"},
|
||||
"PC13": {"NAND_DQ5", "SDC2_D5"},
|
||||
"PC14": {"NAND_DQ6", "SDC2_D6"},
|
||||
"PC15": {"NAND_DQ7", "SDC2_D7"},
|
||||
"PC19": {""},
|
||||
"PD2": {"LCD_D2", "UART2_TX"},
|
||||
"PD3": {"LCD_D3", "UART2_RX"},
|
||||
"PD4": {"LCD_D4", "UART2_CTX"},
|
||||
"PD5": {"LCD_D5", "UART2_RTS"},
|
||||
"PD6": {"LCD_D6", "ECRS"},
|
||||
"PD7": {"LCD_D7", "ECOL"},
|
||||
"PD10": {"LCD_D10", "ERXD0"},
|
||||
"PD11": {"LCD_D11", "ERXD1"},
|
||||
"PD12": {"LCD_D12", "ERXD2"},
|
||||
"PD13": {"LCD_D13", "ERXD3"},
|
||||
"PD14": {"LCD_D14", "ERXCK"},
|
||||
"PD15": {"LCD_D15", "ERXERR"},
|
||||
"PD18": {"LCD_D18", "ERXDV"},
|
||||
"PD19": {"LCD_D19", "ETXD0"},
|
||||
"PD20": {"LCD_D20", "ETXD1"},
|
||||
"PD21": {"LCD_D21", "ETXD2"},
|
||||
"PD22": {"LCD_D22", "ETXD3"},
|
||||
"PD23": {"LCD_D23", "ETXEN"},
|
||||
"PD24": {"LCD_CLK", "ETXCK"},
|
||||
"PD25": {"LCD_DE", "ETXERR"},
|
||||
"PD26": {"LCD_HSYNC", "EMDC"},
|
||||
"PD27": {"LCD_VSYNC", "EMDIO"},
|
||||
"PE0": {"TS_CLK", "CSI_PCLK", "SPI2_CS0", "", "EINT14"},
|
||||
"PE1": {"TS_ERR", "CSI_MCLK", "SPI2_CLK", "", "EINT15"},
|
||||
"PE2": {"TS_SYNC", "CSI_HSYNC", "SPI2_MOSI"},
|
||||
"PE3": {"TS_DVLD", "CSI_VSYNC", "SPI2_MISO"},
|
||||
"PE4": {"TS_D0", "CSI_D0", "SDC2_D0"},
|
||||
"PE5": {"TS_D1", "CSI_D1", "SDC2_D1"},
|
||||
"PE6": {"TS_D2", "CSI_D2", "SDC2_D2"},
|
||||
"PE7": {"TS_D3", "CSI_D3", "SDC2_D3"},
|
||||
"PE8": {"TS_D4", "CSI_D4", "SDC2_CMD"},
|
||||
"PE9": {"TS_D5", "CSI_D5", "SDC2_CLK"},
|
||||
"PE10": {"TS_D6", "CSI_D6", "UART1_TX"},
|
||||
"PE11": {"TS_D7", "CSI_D7", "UART1_RX"},
|
||||
"PF0": {"SDC0_D1", "", "JTAG1_TMS"},
|
||||
"PF1": {"SDC0_D0", "", "JTAG1_TDI"},
|
||||
"PF2": {"SDC0_CLK", "", "UART0_TX"},
|
||||
"PF3": {"SDC0_CMD", "", "JTAG1_TDO"},
|
||||
"PF4": {"SDC0_D3", "", "UART0_RX"},
|
||||
"PF5": {"SDC0_D2", "", "JTAG1_TCK"},
|
||||
"PG0": {"GPS_CLK", "", "", "", "EINT0"},
|
||||
"PG1": {"GPS_SIGN", "", "", "", "EINT1"},
|
||||
"PG2": {"GPS_MAG", "", "", "", "EINT2"},
|
||||
"PG3": {"", "", "UART1_TX", "", "EINT3"},
|
||||
"PG4": {"", "", "UART1_RX", "", "EINT4"},
|
||||
"PG9": {"SPI1_CS0", "UART3_TX", "", "", "EINT9"},
|
||||
"PG10": {"SPI1_CLK", "UART3_RX", "", "", "EINT10"},
|
||||
"PG11": {"SPI1_MOSI", "UART3_CTS", "", "", "EINT11"},
|
||||
"PG12": {"SPI1_MISO", "UART3_RTS", "", "", "EINT12"},
|
||||
}
|
||||
|
||||
// mapR8Pins uses mappingR8 to actually set the altFunc fields of all gpio and
|
||||
// mark them as available.
|
||||
//
|
||||
// It is called by the generic allwinner processor code if a R8 is detected.
|
||||
func mapR8Pins() error {
|
||||
for name, altFuncs := range mappingR8 {
|
||||
pin := cpupins[name]
|
||||
pin.altFunc = altFuncs
|
||||
pin.available = true
|
||||
if strings.Contains(string(altFuncs[4]), "EINT") {
|
||||
pin.supportEdge = true
|
||||
}
|
||||
|
||||
// Initializes the sysfs corresponding pin right away.
|
||||
pin.sysfsPin = sysfs.Pins[pin.Number()]
|
||||
}
|
||||
return nil
|
||||
}
|
207
vendor/periph.io/x/host/v3/allwinner/spi.go
generated
vendored
Normal file
207
vendor/periph.io/x/host/v3/allwinner/spi.go
generated
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package allwinner
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// 31:20 reserved
|
||||
// Set this bit to ‘1’ to make the internal read sample point with a delay of
|
||||
// half cycle of SPI_CLK. It is used in high speed read operation to reduce
|
||||
// the error caused by the time delay of SPI_CLK propagating between master
|
||||
// and slave.
|
||||
// 1 – delay internal read sample point
|
||||
// 0 – normal operation, do not delay internal read sample point
|
||||
spiR8HalfDelay spiR8Ctl = 1 << 19 // Master Sample Data Control
|
||||
spiR8TransmitPause spiR8Ctl = 1 << 18 // Transmit Pause Enable
|
||||
spiR8CSLevel spiR8Ctl = 1 << 17 // SS_LEVEL; Chip Select level
|
||||
spiR8CSManual spiR8Ctl = 1 << 16 // SS_CTRL; Do not switch CS automatically
|
||||
spiR8DiscardHash spiR8Ctl = 1 << 15 // DHB
|
||||
spiR8DummyBurst spiR8Ctl = 1 << 14 // DDB
|
||||
spiR8CS0 spiR8Ctl = 0 << 12 // SS; Which CS line to use. For SPI0 only
|
||||
spiR8CS1 spiR8Ctl = 1 << 12 //
|
||||
spiR8CS2 spiR8Ctl = 2 << 12 //
|
||||
spiR8CS3 spiR8Ctl = 3 << 12 //
|
||||
spiR8RapidsReadMode spiR8Ctl = 1 << 11 // RPSM
|
||||
spiR8ExchangeBurst spiR8Ctl = 1 << 10 // XCH
|
||||
spiR8RXFIFOReset spiR8Ctl = 1 << 9 // RXFIFO Reset; Write to reset the FIFO as empty
|
||||
spiR8TXFIFOReset spiR8Ctl = 1 << 8 // TXFIFO Reset; Write to reset the FIFO as empty
|
||||
spiR8CSBetweenBursts spiR8Ctl = 1 << 7 // SSCTL
|
||||
spiR8LSB spiR8Ctl = 1 << 6 // LMTF; MSB by default, LSB when set
|
||||
spiR8DDMA spiR8Ctl = 1 << 5 // DMAM; Use dedicated DMA if set, normal DMA otherwise
|
||||
spiR8CSActiveLow spiR8Ctl = 1 << 4 // SSPOL; CS line polarity
|
||||
spiR8ClkActiveLow spiR8Ctl = 1 << 3 // POL; Clock line polarity
|
||||
spiR8PHA spiR8Ctl = 1 << 2 // PHA; Phase 1 if set (leading edge for setup data)
|
||||
spiR8Master spiR8Ctl = 1 << 1 // MODE; Slave mode if not set
|
||||
spiR8Enable spiR8Ctl = 1 << 0 // EN; Enable mode
|
||||
)
|
||||
|
||||
// SPI_CTL
|
||||
// R8: Page 153-155. Default: 0x0002001C
|
||||
type spiR8Ctl uint32
|
||||
|
||||
// SPI_INTCTL
|
||||
// R8: Page 155-156.
|
||||
type spiR8IntCtl uint32
|
||||
|
||||
const (
|
||||
spiR8ClearInterrupt spiR8IntStatus = 1 << 31 // Clear interrupt busy flag
|
||||
// 30:18 reserved
|
||||
spiR8InvalidSS spiR8IntStatus = 1 << 17 // SSI
|
||||
spiR8TC spiR8IntStatus = 1 << 16 // TC; Transfer Completed
|
||||
)
|
||||
|
||||
// SPI_INT_STA
|
||||
// R8: Page 156-157.
|
||||
type spiR8IntStatus uint32
|
||||
|
||||
const (
|
||||
// 31:13 reserved
|
||||
spiR8DMATX3Quarter spiR8DMACtl = 1 << 12 // TXFIFO 3/4 empty
|
||||
spiR8DMATX1Quarter spiR8DMACtl = 1 << 11 // TXFIFO 1/4 empty
|
||||
spiR8DMATXByte spiR8DMACtl = 1 << 10 // TXFIFO Not Full
|
||||
spiR8DMATXHalf spiR8DMACtl = 1 << 9 // TXFIFO 1/2 empty
|
||||
spiR8DMATXEmpty spiR8DMACtl = 1 << 8 // TXFIFO empty
|
||||
// 7:5 reserved
|
||||
spiR8DMARX3Quarter spiR8DMACtl = 1 << 4 // RXFIFO 3/4 empty
|
||||
spiR8DMARX1Quarter spiR8DMACtl = 1 << 3 // RXFIFO 1/4 empty
|
||||
spiR8DMARXByte spiR8DMACtl = 1 << 2 // RXFIFO Not Full
|
||||
spiR8DMARXHalf spiR8DMACtl = 1 << 1 // RXFIFO 1/2 empty
|
||||
spiR8DMARXEmpty spiR8DMACtl = 1 << 0 // RXFIFO empty
|
||||
)
|
||||
|
||||
// SPI_DMACTL
|
||||
// R8: Page 158.
|
||||
type spiR8DMACtl uint32
|
||||
|
||||
const (
|
||||
// 31:13 reserved
|
||||
spiR8DivRateSelect2 spiR8ClockCtl = 1 << 12 // DRS; Use spiDivXX if set, use mask otherwise
|
||||
spiR8Div2 spiR8ClockCtl = 0 << 8 // CDR1; Use divisor 2^(n+1)
|
||||
spiR8Div4 spiR8ClockCtl = 1 << 8 //
|
||||
spiR8Div8 spiR8ClockCtl = 2 << 8 //
|
||||
spiR8Div16 spiR8ClockCtl = 3 << 8 //
|
||||
spiR8Div32 spiR8ClockCtl = 4 << 8 //
|
||||
spiR8Div64 spiR8ClockCtl = 5 << 8 //
|
||||
spiR8Div128 spiR8ClockCtl = 6 << 8 //
|
||||
spiR8Div256 spiR8ClockCtl = 7 << 8 //
|
||||
spiR8Div512 spiR8ClockCtl = 8 << 8 //
|
||||
spiR8Div1024 spiR8ClockCtl = 9 << 8 //
|
||||
spiR8Div2048 spiR8ClockCtl = 10 << 8 //
|
||||
spiR8Div4096 spiR8ClockCtl = 11 << 8 //
|
||||
spiR8Div8192 spiR8ClockCtl = 12 << 8 //
|
||||
spiR8Div16384 spiR8ClockCtl = 13 << 8 //
|
||||
spiR8Div32768 spiR8ClockCtl = 14 << 8 //
|
||||
spiR8Div65536 spiR8ClockCtl = 15 << 8 //
|
||||
spiR8Div1Mask spiR8ClockCtl = 0xFF // CDR2; Use divisor 2*(n+1)
|
||||
)
|
||||
|
||||
// SPI_CCTL
|
||||
// R8: Page 159.
|
||||
type spiR8ClockCtl uint32
|
||||
|
||||
const (
|
||||
// 31:25 reserved
|
||||
spiR8FIFOTXShift = 16 // 0 to 64
|
||||
// 15:7 reserved
|
||||
spiR8FIFORXShift = 0 // 0 to 64
|
||||
)
|
||||
|
||||
// SPI_FIFO_STA
|
||||
// R8: Page 160.
|
||||
type spiR8FIFOStatus uint32
|
||||
|
||||
func (s spiR8FIFOStatus) tx() uint8 {
|
||||
return uint8((uint32(s) >> 16) & 127)
|
||||
}
|
||||
|
||||
func (s spiR8FIFOStatus) rx() uint8 {
|
||||
return uint8(uint32(s) & 127)
|
||||
}
|
||||
|
||||
// spiR8Group is the mapping of SPI registers for one SPI controller.
|
||||
// R8: Page 152-153.
|
||||
type spiR8Group struct {
|
||||
rx uint32 // 0x00 SPI_RX_DATA RX Data
|
||||
tx uint32 // 0x04 SPI_TX_DATA TX Data
|
||||
ctl spiR8Ctl // 0x08 SPI_CTL Control
|
||||
intCtl spiR8IntCtl // 0x0C SPI_INTCTL Interrupt Control
|
||||
status spiR8IntStatus // 0x10 SPI_ST Status
|
||||
dmaCtl spiR8DMACtl // 0x14 SPI_DMACTL DMA Control
|
||||
wait uint32 // 0x18 SPI_WAIT Clock Counter; 16 bits
|
||||
clockCtl spiR8ClockCtl // 0x1C SPI_CCTL Clock Rate Control
|
||||
burstCounter uint32 // 0x20 SPI_BC Burst Counter; 24 bits
|
||||
transmitCounter uint32 // 0x24 SPI_TC Transmit Counter; 24 bits
|
||||
fifoStatus spiR8FIFOStatus // 0x28 SPI_FIFO_STA FIFO Status
|
||||
reserved [(0x1000 - 0x02C) / 4]uint32
|
||||
}
|
||||
|
||||
func (s *spiR8Group) setup() {
|
||||
s.intCtl = 0
|
||||
s.status = 0
|
||||
//s.dmaCtl = spiR8DMARXByte
|
||||
s.dmaCtl = 0
|
||||
s.wait = 2
|
||||
s.clockCtl = spiR8DivRateSelect2 | spiR8Div1024
|
||||
// spiR8DDMA
|
||||
s.ctl = spiR8CSManual | spiR8LSB | spiR8Master | spiR8Enable
|
||||
}
|
||||
|
||||
// spiMap is the mapping of SPI registers.
|
||||
// R8: Page 152-153.
|
||||
type spiMap struct {
|
||||
groups [3]spiR8Group
|
||||
}
|
||||
|
||||
// spi2Write do a write on SPI2_MOSI via polling.
|
||||
func spi2Write(w []byte) error {
|
||||
if drvDMA.clockMemory == nil || drvDMA.spiMemory == nil {
|
||||
return errors.New("subsystem not initialized")
|
||||
}
|
||||
// Make sure the source clock is disabled. Set it at 250kHz.
|
||||
//drvDMA.clockMemory.spi2Clk &^= clockSPIEnable
|
||||
drvDMA.clockMemory.spi2Clk |= clockSPIEnable
|
||||
drvDMA.clockMemory.spi2Clk = clockSPIDiv8a | clockSPIDiv12b
|
||||
ch := &drvDMA.spiMemory.groups[2]
|
||||
ch.setup()
|
||||
fmt.Printf("Setup done\n")
|
||||
for i := 0; i < len(w)/4; i++ {
|
||||
// TODO(maruel): Access it in 8bit mode.
|
||||
ch.tx = uint32(w[0])
|
||||
for ch.fifoStatus.tx() == 0 {
|
||||
log.Printf("Waiting for bit %# v\n", ch)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
fmt.Printf("Done\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
// spi2Read do a read on SPI2_MISO via polling.
|
||||
func spi2Read(r []byte) error {
|
||||
if drvDMA.clockMemory == nil || drvDMA.spiMemory == nil {
|
||||
return errors.New("subsystem not initialized")
|
||||
}
|
||||
// Make sure the source clock is disabled. Set it at 250kHz.
|
||||
//drvDMA.clockMemory.spi2Clk &^= clockSPIEnable
|
||||
drvDMA.clockMemory.spi2Clk |= clockSPIEnable
|
||||
drvDMA.clockMemory.spi2Clk = clockSPIDiv8a | clockSPIDiv12b
|
||||
ch := &drvDMA.spiMemory.groups[2]
|
||||
ch.setup()
|
||||
for i := 0; i < len(r)/4; i++ {
|
||||
ch.tx = 0
|
||||
for ch.status&spiR8TC == 0 {
|
||||
}
|
||||
// TODO(maruel): Access it in 8bit mode.
|
||||
r[i] = uint8(ch.rx)
|
||||
}
|
||||
fmt.Printf("Done\n")
|
||||
return nil
|
||||
}
|
128
vendor/periph.io/x/host/v3/allwinner/timer.go
generated
vendored
Normal file
128
vendor/periph.io/x/host/v3/allwinner/timer.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package allwinner
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"periph.io/x/host/v3/cpu"
|
||||
)
|
||||
|
||||
// ReadTime returns the time on a monotonic timer.
|
||||
//
|
||||
// It only works if allwinner-dma successfully loaded. Otherwise it returns 0.
|
||||
func ReadTime() time.Duration {
|
||||
if drvDMA.timerMemory == nil {
|
||||
return 0
|
||||
}
|
||||
v := uint64(drvDMA.timerMemory.counterHigh)<<32 | uint64(drvDMA.timerMemory.counterLow)
|
||||
if v == 0 {
|
||||
// BUG(maruel): Implement using AVS_CNT0_REG on A64.
|
||||
return 0
|
||||
}
|
||||
// BUG(maruel): Assumes that counterCtrl & timerPLL6 is not set.
|
||||
const tick = time.Microsecond / 24
|
||||
return time.Duration(v) * tick
|
||||
}
|
||||
|
||||
// Nanospin spins the CPU without calling into the kernel code if possible.
|
||||
func Nanospin(t time.Duration) {
|
||||
start := ReadTime()
|
||||
if start == 0 {
|
||||
// Use the slow generic version.
|
||||
cpu.Nanospin(t)
|
||||
return
|
||||
}
|
||||
for ReadTime()-start < t {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const (
|
||||
// 31:3 reserved
|
||||
timerPLL6 timerCtrl = 2 << 1 // CONT64_CLK_SRC_SEL; OSC24M if not set;
|
||||
timerReadLatchEnable timerCtrl = 1 << 1 // CONT64_RLATCH_EN; 1 to latch the counter to the registers
|
||||
timerClear = 1 << 0 // CONT64_CLR_EN; clears the counter
|
||||
)
|
||||
|
||||
// R8: Page 96
|
||||
type timerCtrl uint32
|
||||
|
||||
// timerMap is the mapping of important registers across CPUs.
|
||||
type timerMap struct {
|
||||
reserved0 [0x80 / 4]uint32 //
|
||||
cntCtl timerCtrl // 0x80 AVS_CNT_CTL_REG AVS Control Register
|
||||
cnt0 uint32 // 0x84 AVS_CNT0_REG AVS Counter 0 Register
|
||||
cnt1 uint32 // 0x88 AVS_CNT1_REG AVS Counter 1 Register
|
||||
cndDrv uint32 // 0x8C AVS_CNT_DIV_REG AVS Divisor Register
|
||||
reserved1 [0x10 / 4]uint32 // On R8 only.
|
||||
counterCtrl timerCtrl // 0x0A0 COUNTER64_CTRL_REG 64-bit Counter control
|
||||
counterLow uint32 // 0x0A4 COUNTER64_LOW_REG 64-bit Counter low
|
||||
counterHigh uint32 // 0x0A8 COUNTER64_HI_REG 64-bit Counter high
|
||||
}
|
||||
|
||||
// A64: Page 161.
|
||||
type timerMapA64 struct {
|
||||
reserved0 uint32 // 0x0 TMR_IRQ_EN_REG Timer IRQ Enable Register
|
||||
reserved1 uint32 // 0x4 TMR_IRQ_STA_REG Timer Status Register
|
||||
reserved2 uint32 // 0x10 TMR0_CTRL_REG Timer 0 Control Register
|
||||
reserved3 uint32 // 0x14 TMR0_INTV_VALUE_REG Timer 0 Interval Value Register
|
||||
reserved4 uint32 // 0x18 TMR0_CUR_VALUE_REG Timer 0 Current Value Register
|
||||
reserved5 uint32 // 0x20 TMR1_CTRL_REG Timer 1 Control Register
|
||||
reserved6 uint32 // 0x24 TMR1_INTV_VALUE_REG Timer 1 Interval Value Register
|
||||
reserved7 uint32 // 0x28 TMR1_CUR_VALUE_REG Timer 1 Current Value Register
|
||||
cntCtl timerCtrl // 0x80 AVS_CNT_CTL_REG AVS Control Register
|
||||
cnt0 uint32 // 0x84 AVS_CNT0_REG AVS Counter 0 Register
|
||||
cnt1 uint32 // 0x88 AVS_CNT1_REG AVS Counter 1 Register
|
||||
cndDrv uint32 // 0x8C AVS_CNT_DIV_REG AVS Divisor Register
|
||||
reserved8 uint32 // 0xA0 WDOG0_IRQ_EN_REG Watchdog 0 IRQ Enable Register
|
||||
reserved9 uint32 // 0xA4 WDOG0_IRQ_STA_REG Watchdog 0 Status Register
|
||||
reserved10 uint32 // 0xB0 WDOG0_CTRL_REG Watchdog 0 Control Register
|
||||
reserved11 uint32 // 0xB4 WDOG0_CFG_REG Watchdog 0 Configuration Register
|
||||
reserved12 uint32 // 0xB8 WDOG0_MODE_REG Watchdog 0 Mode Register
|
||||
}
|
||||
|
||||
// R8: Page 85
|
||||
type timerMapR8 struct {
|
||||
reserved0 uint32 // 0x000 ASYNC_TMR_IRQ_EN_REG Timer IRQ Enable
|
||||
reserved1 uint32 // 0x004 ASYNC_TMR_IRQ_STAS_REG Timer Status
|
||||
reserved2 [2]uint32 // 0x008-0x00C
|
||||
reserved3 uint32 // 0x010 ASYNC_TMR0_CTRL_REG Timer 0 Control
|
||||
reserved4 uint32 // 0x014 ASYNC_TMR0_INTV_VALUE_REG Timer 0 Interval Value
|
||||
reserved5 uint32 // 0x018 ASYNC_TMR0_CURNT_VALUE_REG Timer 0 Current Value
|
||||
reserved6 uint32 // 0x01C
|
||||
reserved7 uint32 // 0x020 ASYNC_TMR1_CTRL_REG Timer 1 Control
|
||||
reserved8 uint32 // 0x024 ASYNC_TMR1_INTV_VALUE_REG Timer 1 Interval Value
|
||||
reserved9 uint32 // 0x028 ASYNC_TMR1_CURNT_VALUE_REG Timer 1 Current Value
|
||||
reserved10 uint32 // 0x02C
|
||||
reserved11 uint32 // 0x030 ASYNC_TMR2_CTRL_REG Timer 2 Control
|
||||
reserved12 uint32 // 0x034 ASYNC_TMR2_INTV_VALUE_REG Timer 2 Interval Value
|
||||
reserved13 uint32 // 0x038 ASYNC_TMR2_CURNT_VALUE_REG Timer 2 Current Value
|
||||
reserved14 uint32 // 0x03C
|
||||
reserved15 uint32 // 0x040 ASYNC_TMR3_CTRL_REG Timer 3 Control
|
||||
reserved16 uint32 // 0x044 ASYNC_TMR3_INTV_VALUE_REG Timer 3 Interval Value
|
||||
reserved17 [2]uint32 // 0x048-0x04C
|
||||
reserved18 uint32 // 0x050 ASYNC_TMR4_CTRL_REG Timer 4 Control
|
||||
reserved19 uint32 // 0x054 ASYNC_TMR4_INTV_VALUE_REG Timer 4 Interval Value
|
||||
reserved20 uint32 // 0x058 ASYNC_TMR4_CURNT_VALUE_REG Timer 4 Current Value
|
||||
reserved21 uint32 // 0x05C
|
||||
reserved22 uint32 // 0x060 ASYNC_TMR5_CTRL_REG Timer 5 Control
|
||||
reserved23 uint32 // 0x064 ASYNC_TMR5_INTV_VALUE_REG Timer 5 Interval Value
|
||||
reserved24 uint32 // 0x068 ASYNC_TMR5_CURNT_VALUE_REG Timer 5 Current Value
|
||||
reserved25 [5]uint32 // 0x06C-0x07C
|
||||
cntCtl timerCtrl // 0x080 AVS_CNT_CTL_REG AVS Control Register
|
||||
cnt0 uint32 // 0x084 AVS_CNT0_REG AVS Counter 0 Register
|
||||
cnt1 uint32 // 0x088 AVS_CNT1_REG AVS Counter 1 Register
|
||||
cndDiv uint32 // 0x08C AVS_CNT_DIVISOR_REG AVS Divisor
|
||||
reserved26 uint32 // 0x090 WDOG_CTRL_REG
|
||||
reserved27 uint32 // 0x094 WDOG_MODE_REG Watchdog Mode
|
||||
reserved28 [2]uint32 // 0x098-0x09C
|
||||
counterCtrl timerCtrl // 0x0A0 COUNTER64_CTRL_REG 64-bit Counter control
|
||||
counterLow uint32 // 0x0A4 COUNTER64_LOW_REG 64-bit Counter low
|
||||
counterHigh uint32 // 0x0A8 COUNTER64_HI_REG 64-bit Counter high
|
||||
reserved29 [0x94]uint32 // 0x0AC-0x13C
|
||||
reserved30 uint32 // 0x140 CPU_CFG_REG CPU configuration register
|
||||
}
|
52
vendor/periph.io/x/host/v3/am335x/am335x.go
generated
vendored
Normal file
52
vendor/periph.io/x/host/v3/am335x/am335x.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package am335x
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"periph.io/x/conn/v3/driver/driverreg"
|
||||
"periph.io/x/host/v3/distro"
|
||||
)
|
||||
|
||||
// Present returns true if a TM AM335x processor is detected.
|
||||
func Present() bool {
|
||||
if isArm {
|
||||
return strings.HasPrefix(distro.DTModel(), "TI AM335x")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// driver implements periph.Driver.
|
||||
type driver struct {
|
||||
}
|
||||
|
||||
func (d *driver) String() string {
|
||||
return "am335x"
|
||||
}
|
||||
|
||||
func (d *driver) Prerequisites() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) After() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) Init() (bool, error) {
|
||||
if !Present() {
|
||||
return false, errors.New("am335x CPU not detected")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
if isArm {
|
||||
driverreg.MustRegister(&drv)
|
||||
}
|
||||
}
|
||||
|
||||
var drv driver
|
7
vendor/periph.io/x/host/v3/am335x/am335x_arm.go
generated
vendored
Normal file
7
vendor/periph.io/x/host/v3/am335x/am335x_arm.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package am335x
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/am335x/am335x_other.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/am335x/am335x_other.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm
|
||||
|
||||
package am335x
|
||||
|
||||
const isArm = false
|
28
vendor/periph.io/x/host/v3/am335x/doc.go
generated
vendored
Normal file
28
vendor/periph.io/x/host/v3/am335x/doc.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package am335x exposes functionality for the Texas Instruments Sitara AM335x
|
||||
// processor family.
|
||||
//
|
||||
// This processor family is found on the BeagleBone. PRU-ICSS functionality is
|
||||
// implemented in package pru.
|
||||
//
|
||||
// The GPIO pins of the AM335x CPU are grouped into 3 groups of 32 pins: GPIO0,
|
||||
// GPIO1, and GPIO2. The CPU documentation refers to GPIO in the form of
|
||||
// GPIOx_y. To get the absolute number, as exposed by sysfs, use 32*x+y to get
|
||||
// the absolute number.
|
||||
//
|
||||
// Datasheet
|
||||
//
|
||||
// Technical Reference Manual
|
||||
// https://www.ti.com/lit/ug/spruh73p/spruh73p.pdf
|
||||
//
|
||||
// Other
|
||||
//
|
||||
// Marketing page
|
||||
// https://www.ti.com/processors/sitara/arm-cortex-a8/am335x/overview.html
|
||||
//
|
||||
// Family overview
|
||||
// https://www.ti.com/lit/ds/symlink/am3359.pdf
|
||||
package am335x
|
7
vendor/periph.io/x/host/v3/bcm283x/bcm283x_arm.go
generated
vendored
Normal file
7
vendor/periph.io/x/host/v3/bcm283x/bcm283x_arm.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package bcm283x
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/bcm283x/bcm283x_arm64.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/bcm283x/bcm283x_arm64.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build arm64
|
||||
|
||||
package bcm283x
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/bcm283x/bcm283x_other.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/bcm283x/bcm283x_other.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm,!arm64
|
||||
|
||||
package bcm283x
|
||||
|
||||
const isArm = false
|
330
vendor/periph.io/x/host/v3/bcm283x/clock.go
generated
vendored
Normal file
330
vendor/periph.io/x/host/v3/bcm283x/clock.go
generated
vendored
Normal file
@ -0,0 +1,330 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package bcm283x
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"periph.io/x/conn/v3/physic"
|
||||
)
|
||||
|
||||
// errClockRegister is returned in a situation where the clock memory is not
|
||||
// working as expected. It is mocked in tests.
|
||||
var errClockRegister = errors.New("can't write to clock divisor CPU register")
|
||||
|
||||
// Clock sources frequency in hertz.
|
||||
const (
|
||||
clk19dot2MHz = 19200 * physic.KiloHertz
|
||||
clk500MHz = 500 * physic.MegaHertz
|
||||
)
|
||||
|
||||
const (
|
||||
// 31:24 password
|
||||
clockPasswdCtl clockCtl = 0x5A << 24 // PASSWD
|
||||
// 23:11 reserved
|
||||
clockMashMask clockCtl = 3 << 9 // MASH
|
||||
clockMash0 clockCtl = 0 << 9 // src_freq / divI (ignores divF)
|
||||
clockMash1 clockCtl = 1 << 9
|
||||
clockMash2 clockCtl = 2 << 9
|
||||
clockMash3 clockCtl = 3 << 9 // will cause higher spread
|
||||
clockFlip clockCtl = 1 << 8 // FLIP
|
||||
clockBusy clockCtl = 1 << 7 // BUSY
|
||||
// 6 reserved
|
||||
clockKill clockCtl = 1 << 5 // KILL
|
||||
clockEnable clockCtl = 1 << 4 // ENAB
|
||||
clockSrcMask clockCtl = 0xF << 0 // SRC
|
||||
clockSrcGND clockCtl = 0 // 0Hz
|
||||
clockSrc19dot2MHz clockCtl = 1 // 19.2MHz
|
||||
clockSrcTestDebug0 clockCtl = 2 // 0Hz
|
||||
clockSrcTestDebug1 clockCtl = 3 // 0Hz
|
||||
clockSrcPLLA clockCtl = 4 // 0Hz
|
||||
clockSrcPLLC clockCtl = 5 // 1000MHz (changes with overclock settings)
|
||||
clockSrcPLLD clockCtl = 6 // 500MHz
|
||||
clockSrcHDMI clockCtl = 7 // 216MHz; may be disabled
|
||||
// 8-15 == GND.
|
||||
)
|
||||
|
||||
// clockCtl controls the clock properties.
|
||||
//
|
||||
// It must not be changed while busy is set or a glitch may occur.
|
||||
//
|
||||
// Page 107
|
||||
type clockCtl uint32
|
||||
|
||||
func (c clockCtl) String() string {
|
||||
var out []string
|
||||
if c&0xFF000000 == clockPasswdCtl {
|
||||
c &^= 0xFF000000
|
||||
out = append(out, "PWD")
|
||||
}
|
||||
switch c & clockMashMask {
|
||||
case clockMash1:
|
||||
out = append(out, "Mash1")
|
||||
case clockMash2:
|
||||
out = append(out, "Mash2")
|
||||
case clockMash3:
|
||||
out = append(out, "Mash3")
|
||||
default:
|
||||
}
|
||||
c &^= clockMashMask
|
||||
if c&clockFlip != 0 {
|
||||
out = append(out, "Flip")
|
||||
c &^= clockFlip
|
||||
}
|
||||
if c&clockBusy != 0 {
|
||||
out = append(out, "Busy")
|
||||
c &^= clockBusy
|
||||
}
|
||||
if c&clockKill != 0 {
|
||||
out = append(out, "Kill")
|
||||
c &^= clockKill
|
||||
}
|
||||
if c&clockEnable != 0 {
|
||||
out = append(out, "Enable")
|
||||
c &^= clockEnable
|
||||
}
|
||||
switch x := c & clockSrcMask; x {
|
||||
case clockSrcGND:
|
||||
out = append(out, "GND(0Hz)")
|
||||
case clockSrc19dot2MHz:
|
||||
out = append(out, "19.2MHz")
|
||||
case clockSrcTestDebug0:
|
||||
out = append(out, "Debug0(0Hz)")
|
||||
case clockSrcTestDebug1:
|
||||
out = append(out, "Debug1(0Hz)")
|
||||
case clockSrcPLLA:
|
||||
out = append(out, "PLLA(0Hz)")
|
||||
case clockSrcPLLC:
|
||||
out = append(out, "PLLD(1000MHz)")
|
||||
case clockSrcPLLD:
|
||||
out = append(out, "PLLD(500MHz)")
|
||||
case clockSrcHDMI:
|
||||
out = append(out, "HDMI(216MHz)")
|
||||
default:
|
||||
out = append(out, fmt.Sprintf("GND(%d)", x))
|
||||
}
|
||||
c &^= clockSrcMask
|
||||
if c != 0 {
|
||||
out = append(out, fmt.Sprintf("clockCtl(0x%0x)", uint32(c)))
|
||||
}
|
||||
return strings.Join(out, "|")
|
||||
}
|
||||
|
||||
const (
|
||||
// 31:24 password
|
||||
clockPasswdDiv clockDiv = 0x5A << 24 // PASSWD
|
||||
// Integer part of the divisor
|
||||
clockDiviShift = 12
|
||||
clockDiviMax = (1 << 12) - 1
|
||||
clockDiviMask clockDiv = clockDiviMax << clockDiviShift // DIVI
|
||||
// Fractional part of the divisor
|
||||
clockDivfMask clockDiv = (1 << 12) - 1 // DIVF
|
||||
)
|
||||
|
||||
// clockDiv is a 12.12 fixed point value.
|
||||
//
|
||||
// The fractional part generates a significant amount of noise so it is
|
||||
// preferable to not use it.
|
||||
//
|
||||
// Page 108
|
||||
type clockDiv uint32
|
||||
|
||||
func (c clockDiv) String() string {
|
||||
i := (c & clockDiviMask) >> clockDiviShift
|
||||
c &^= clockDiviMask
|
||||
if c == 0 {
|
||||
return fmt.Sprintf("%d.0", i)
|
||||
}
|
||||
return fmt.Sprintf("%d.(%d/%d)", i, c, clockDiviMax)
|
||||
}
|
||||
|
||||
// clock is a pair of clockCtl / clockDiv.
|
||||
//
|
||||
// It can be set to one of the sources: clockSrc19dot2MHz(19.2MHz) and
|
||||
// clockSrcPLLD(500Mhz), then divided to a value to get the resulting clock.
|
||||
// Per spec the resulting frequency should be under 25Mhz.
|
||||
type clock struct {
|
||||
ctl clockCtl
|
||||
div clockDiv
|
||||
}
|
||||
|
||||
// findDivisorExact finds the clock divisor and wait cycles to reduce src to
|
||||
// desired hz.
|
||||
//
|
||||
// The clock divisor is capped to clockDiviMax.
|
||||
//
|
||||
// Returns clock divisor, wait cycles. Returns 0, 0 if no exact match is found.
|
||||
// Favorizes high clock divisor value over high clock wait cycles. This means
|
||||
// that the function is slower than it could be, but results in more stable
|
||||
// clock.
|
||||
func findDivisorExact(src, desired physic.Frequency, maxWaitCycles uint32) (uint32, uint32) {
|
||||
if src < desired || src%desired != 0 || src/physic.Frequency(maxWaitCycles*clockDiviMax) > desired {
|
||||
// Can't attain without oversampling (too low) or desired frequency is
|
||||
// higher than the source (too high) or is not a multiple.
|
||||
return 0, 0
|
||||
}
|
||||
factor := uint32(src / desired)
|
||||
// TODO(maruel): Only iterate over valid divisors to save a bit more
|
||||
// calculations. Since it's is only doing 32 loops, this is not a big deal.
|
||||
for wait := uint32(1); wait <= maxWaitCycles; wait++ {
|
||||
if rest := factor % wait; rest != 0 {
|
||||
continue
|
||||
}
|
||||
clk := factor / wait
|
||||
if clk == 0 {
|
||||
break
|
||||
}
|
||||
if clk <= clockDiviMax {
|
||||
return clk, wait
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// findDivisorOversampled tries to find the lowest allowed oversampling to make
|
||||
// desiredHz a multiple of srcHz.
|
||||
//
|
||||
// Allowed oversampling depends on the desiredHz. Cap oversampling because
|
||||
// oversampling at 10x in the 1Mhz range becomes unreasonable in term of
|
||||
// memory usage.
|
||||
func findDivisorOversampled(src, desired physic.Frequency, maxWaitCycles uint32) (uint32, uint32, physic.Frequency) {
|
||||
//log.Printf("findDivisorOversampled(%s, %s, %d)", src, desired, maxWaitCycles)
|
||||
// There are 2 reasons:
|
||||
// - desired is so low it is not possible to lower src to this frequency
|
||||
// - not a multiple, there's a need for a prime number
|
||||
// TODO(maruel): Rewrite without a loop, this is not needed. Leverage primes
|
||||
// to reduce the number of iterations.
|
||||
for multiple := physic.Frequency(2); ; multiple++ {
|
||||
n := multiple * desired
|
||||
if n > 100*physic.KiloHertz && multiple > 10 {
|
||||
break
|
||||
}
|
||||
if clk, wait := findDivisorExact(src, n, maxWaitCycles); clk != 0 {
|
||||
return clk, wait, n
|
||||
}
|
||||
}
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
// calcSource choose the best source to get the exact desired clock.
|
||||
//
|
||||
// It calculates the clock source, the clock divisor and the wait cycles, if
|
||||
// applicable. Wait cycles is 'div minus 1'.
|
||||
func calcSource(f physic.Frequency, maxWaitCycles uint32) (clockCtl, uint32, uint32, physic.Frequency, error) {
|
||||
if f < physic.Hertz {
|
||||
return 0, 0, 0, 0, fmt.Errorf("bcm283x-clock: desired frequency %s must be >1hz", f)
|
||||
}
|
||||
if f > 125*physic.MegaHertz {
|
||||
return 0, 0, 0, 0, fmt.Errorf("bcm283x-clock: desired frequency %s is too high", f)
|
||||
}
|
||||
// http://elinux.org/BCM2835_datasheet_errata states that clockSrc19dot2MHz
|
||||
// is the cleanest clock source so try it first.
|
||||
div, wait := findDivisorExact(clk19dot2MHz, f, maxWaitCycles)
|
||||
if div != 0 {
|
||||
return clockSrc19dot2MHz, div, wait, f, nil
|
||||
}
|
||||
// Try 500Mhz.
|
||||
div, wait = findDivisorExact(clk500MHz, f, maxWaitCycles)
|
||||
if div != 0 {
|
||||
return clockSrcPLLD, div, wait, f, nil
|
||||
}
|
||||
|
||||
// Try with up to 10x oversampling. This is generally useful for lower
|
||||
// frequencies, below 10kHz. Prefer the one with less oversampling. Only for
|
||||
// non-aliased matches.
|
||||
div19, wait19, f19 := findDivisorOversampled(clk19dot2MHz, f, maxWaitCycles)
|
||||
div500, wait500, f500 := findDivisorOversampled(clk500MHz, f, maxWaitCycles)
|
||||
if div19 != 0 && (div500 == 0 || f19 < f500) {
|
||||
return clockSrc19dot2MHz, div19, wait19, f19, nil
|
||||
}
|
||||
if div500 != 0 {
|
||||
return clockSrcPLLD, div500, wait500, f500, nil
|
||||
}
|
||||
return 0, 0, 0, 0, errors.New("failed to find a good clock")
|
||||
}
|
||||
|
||||
// set changes the clock frequency to the desired value or the closest one
|
||||
// otherwise.
|
||||
//
|
||||
// f=0 means disabled.
|
||||
//
|
||||
// maxWaitCycles is the maximum oversampling via an additional wait cycles that
|
||||
// can further divide the clock. Use 1 if no additional wait cycle is
|
||||
// available. It is expected to be dmaWaitcyclesMax+1.
|
||||
//
|
||||
// Returns the actual clock used and divisor.
|
||||
func (c *clock) set(f physic.Frequency, maxWaitCycles uint32) (physic.Frequency, uint32, error) {
|
||||
if f == 0 {
|
||||
c.ctl = clockPasswdCtl | clockKill
|
||||
for c.ctl&clockBusy != 0 {
|
||||
}
|
||||
return 0, 0, nil
|
||||
}
|
||||
ctl, div, div2, actual, err := calcSource(f, maxWaitCycles)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return actual, div2, c.setRaw(ctl, div)
|
||||
}
|
||||
|
||||
// setRaw sets the clock speed with the clock source and the divisor.
|
||||
func (c *clock) setRaw(ctl clockCtl, div uint32) error {
|
||||
if div < 1 || div > clockDiviMax {
|
||||
return errors.New("invalid clock divisor")
|
||||
}
|
||||
if ctl != clockSrc19dot2MHz && ctl != clockSrcPLLD {
|
||||
return errors.New("invalid clock control")
|
||||
}
|
||||
// Stop the clock.
|
||||
// TODO(maruel): Do not stop the clock if the current clock rate is the one
|
||||
// desired.
|
||||
for c.ctl&clockBusy != 0 {
|
||||
c.ctl = clockPasswdCtl | clockKill
|
||||
}
|
||||
d := clockDiv(div << clockDiviShift)
|
||||
c.div = clockPasswdDiv | d
|
||||
Nanospin(10 * time.Nanosecond)
|
||||
// Page 107
|
||||
c.ctl = clockPasswdCtl | ctl
|
||||
Nanospin(10 * time.Nanosecond)
|
||||
c.ctl = clockPasswdCtl | ctl | clockEnable
|
||||
if c.div != d {
|
||||
// This error is mocked out in tests, so the code path of set() callers can
|
||||
// follow on.
|
||||
return errClockRegister
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *clock) String() string {
|
||||
return fmt.Sprintf("%s / %s", c.ctl, c.div)
|
||||
}
|
||||
|
||||
// clockMap is the memory mapped clock registers.
|
||||
//
|
||||
// The clock #1 must not be touched since it is being used by the ethernet
|
||||
// controller.
|
||||
//
|
||||
// Page 107 for gp0~gp2.
|
||||
// https://scribd.com/doc/127599939/BCM2835-Audio-clocks for PCM/PWM.
|
||||
type clockMap struct {
|
||||
reserved0 [0x70 / 4]uint32 //
|
||||
gp0 clock // CM_GP0CTL+CM_GP0DIV; 0x70-0x74 (125MHz max)
|
||||
gp1ctl uint32 // CM_GP1CTL+CM_GP1DIV; 0x78-0x7A must not use (used by ethernet)
|
||||
gp1div uint32 // CM_GP1CTL+CM_GP1DIV; 0x78-0x7A must not use (used by ethernet)
|
||||
gp2 clock // CM_GP2CTL+CM_GP2DIV; 0x80-0x84 (125MHz max)
|
||||
reserved1 [(0x98 - 0x88) / 4]uint32 // 0x88-0x94
|
||||
pcm clock // CM_PCMCTL+CM_PCMDIV 0x98-0x9C
|
||||
pwm clock // CM_PWMCTL+CM_PWMDIV 0xA0-0xA4
|
||||
}
|
||||
|
||||
func (c *clockMap) GoString() string {
|
||||
return fmt.Sprintf(
|
||||
"{\n gp0: %s,\n gp1: %s,\n gp2: %s,\n pcm: %sw,\n pwm: %s,\n}",
|
||||
&c.gp0, &clock{clockCtl(c.gp1ctl), clockDiv(c.gp1div)}, &c.gp2, &c.pcm, &c.pwm)
|
||||
}
|
1258
vendor/periph.io/x/host/v3/bcm283x/dma.go
generated
vendored
Normal file
1258
vendor/periph.io/x/host/v3/bcm283x/dma.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
42
vendor/periph.io/x/host/v3/bcm283x/doc.go
generated
vendored
Normal file
42
vendor/periph.io/x/host/v3/bcm283x/doc.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package bcm283x exposes the BCM283x GPIO functionality.
|
||||
//
|
||||
// This driver implements memory-mapped GPIO pin manipulation and leverages
|
||||
// sysfs-gpio for edge detection.
|
||||
//
|
||||
// If you are looking for the actual implementation, open doc.go for further
|
||||
// implementation details.
|
||||
//
|
||||
// GPIOs
|
||||
//
|
||||
// Aliases for GPCLK0, GPCLK1, GPCLK2 are created for corresponding CLKn pins.
|
||||
// Same for PWM0_OUT and PWM1_OUT, which point respectively to PWM0 and PWM1.
|
||||
//
|
||||
// Datasheet
|
||||
//
|
||||
// https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
|
||||
//
|
||||
// Its crowd-sourced errata: http://elinux.org/BCM2835_datasheet_errata
|
||||
//
|
||||
// BCM2836:
|
||||
// https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
|
||||
//
|
||||
// Another doc about PCM and PWM:
|
||||
// https://scribd.com/doc/127599939/BCM2835-Audio-clocks
|
||||
//
|
||||
// GPIO pad control:
|
||||
// https://scribd.com/doc/101830961/GPIO-Pads-Control2
|
||||
package bcm283x
|
||||
|
||||
// Other implementations details
|
||||
//
|
||||
// mainline:
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/dma/bcm2835-dma.c
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/gpio
|
||||
//
|
||||
// Raspbian kernel:
|
||||
// https://github.com/raspberrypi/linux/blob/rpi-4.11.y/drivers/dma
|
||||
// https://github.com/raspberrypi/linux/blob/rpi-4.11.y/drivers/gpio
|
1443
vendor/periph.io/x/host/v3/bcm283x/gpio.go
generated
vendored
Normal file
1443
vendor/periph.io/x/host/v3/bcm283x/gpio.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
234
vendor/periph.io/x/host/v3/bcm283x/pcm.go
generated
vendored
Normal file
234
vendor/periph.io/x/host/v3/bcm283x/pcm.go
generated
vendored
Normal file
@ -0,0 +1,234 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// pcm means I2S.
|
||||
|
||||
package bcm283x
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"periph.io/x/conn/v3/physic"
|
||||
)
|
||||
|
||||
type pcmCS uint32
|
||||
|
||||
// Pages 126-129
|
||||
const (
|
||||
// 31:26 reserved
|
||||
pcmStandby pcmCS = 1 << 25 // STBY Allow at least 4 PCM clock cycles to take effect
|
||||
pcmSync pcmCS = 1 << 24 // SYNC Two PCM clocks have occurred since last write
|
||||
pcmRXSignExtend pcmCS = 1 << 23 // RXSEX Sign extend RXZ data
|
||||
pcmRXFull pcmCS = 1 << 22 // RXF RX FIFO is full
|
||||
pcmTXEmpty pcmCS = 1 << 21 // TXE TX FIFO is empty
|
||||
pcmRXData pcmCS = 1 << 20 // RXD RX FIFO contains data
|
||||
pcmTXData pcmCS = 1 << 19 // TXD TX FIFO ready to accept data
|
||||
pcmRXR pcmCS = 1 << 18 // RXR RX FIFO needs reading
|
||||
pcmTXW pcmCS = 1 << 17 // TXW TX FIFO needs writing
|
||||
pcmRXErr pcmCS = 1 << 16 // RXERR RX FIFO error
|
||||
pcmTXErr pcmCS = 1 << 15 // TXERR TX FIFO error
|
||||
pcmRXSync pcmCS = 1 << 14 // RXSYNC RX FIFO is out of sync
|
||||
pcmTXSync pcmCS = 1 << 13 // TXSYNC TX FIFO is out of sync
|
||||
// 12:10 reserved
|
||||
pcmDMAEnable pcmCS = 1 << 9 // DMAEN Generate TX&RX DMA DREQ
|
||||
// 8:7 RXTHR controls when pcmRXR is set
|
||||
pcmRXThresholdOne pcmCS = 0 << 7 // One sample in RX FIFO
|
||||
pcmRXThreshold1 pcmCS = 1 << 7 // RX FIFO is at least (?) full
|
||||
pcmRXThreshold2 pcmCS = 2 << 7 // ?
|
||||
pcmRXThresholdFull pcmCS = 3 << 7 // RX is full
|
||||
// 6:5 TXTHR controls when pcmTXW is set
|
||||
pcmTXThresholdEmpty pcmCS = 0 << 5 // TX FIFO is empty
|
||||
pcmTXThresholdNotFull1 pcmCS = 1 << 5 // At least one sample can be put
|
||||
pcmTXThresholdNotFull2 pcmCS = 2 << 5 // At least one sample can be put
|
||||
pcmTXThresholdOne pcmCS = 3 << 5 // One sample can be put
|
||||
pcmRXClear pcmCS = 1 << 4 // RXCLR Clear RX FIFO; takes 2 PCM clock to take effect
|
||||
pcmTXClear pcmCS = 1 << 3 // TXCLR Clear TX FIFO; takes 2 PCM clock to take effect
|
||||
pcmTXEnable pcmCS = 1 << 2 // TXON Enable TX
|
||||
pcmRXEnable pcmCS = 1 << 1 // RXON Enable FX
|
||||
pcmEnable pcmCS = 1 << 0 // EN Enable the PCM
|
||||
)
|
||||
|
||||
type pcmMode uint32
|
||||
|
||||
// Page 129-131
|
||||
const (
|
||||
// 31:29 reserved
|
||||
pcmClockDisable pcmMode = 1 << 28 // CLK_DIS Cleanly disable the PCM clock
|
||||
pcmDecimation32 pcmMode = 1 << 27 // PDMN; 0 is factor 16, 1 is factor 32
|
||||
pcmRXPDMFilter pcmMode = 1 << 26 // PDME Enable input CIC filter on PDM input
|
||||
pcmRXMerge pcmMode = 1 << 25 // FRXP Merge both channels as single FIFO entry
|
||||
pcmTXMerge pcmMode = 1 << 24 // FTXP Merge both channels as singe FIFO entry
|
||||
pcmClockSlave pcmMode = 1 << 23 // CLKM PCM CLK is input
|
||||
pcmClockInverted pcmMode = 1 << 22 // CLKI Inverse clock signal
|
||||
pcmFSSlave pcmMode = 1 << 21 // FSM PCM FS is input
|
||||
pcmFSInverted pcmMode = 1 << 20 // FSI Invese FS signal
|
||||
pcmFrameLengthShift = 10 //
|
||||
pcmFrameLenghtMask pcmMode = 0x3F << pcmFrameLengthShift // FLEN Frame length + 1
|
||||
pcmFSLenghtMask pcmMode = 0x3F << 0 // FSLEN FS pulse clock width
|
||||
)
|
||||
|
||||
type pcmRX uint32
|
||||
|
||||
// Page 131-132
|
||||
const (
|
||||
pcmRX1Width pcmRX = 1 << 31 // CH1WEX Legacy
|
||||
pcmRX1Enable pcmRX = 1 << 30 // CH1EN
|
||||
pcmRX1PosShift = 20
|
||||
pcmRX1PosMask pcmRX = 0x3F << pcmRX1PosShift // CH1POS Clock delay
|
||||
pcmRX1Channel16 pcmRX = 8 << 16 // CH1WID (Arbitrary width between 8 and 16 is supported)
|
||||
pcmRX2Width pcmRX = 1 << 15 // CH2WEX Legacy
|
||||
pcmRX2Enable pcmRX = 1 << 14 // CH2EN
|
||||
pcmRX2PosShift = 4
|
||||
pcmRX2PosMask pcmRX = 0x3F << pcmRX2PosShift // CH2POS Clock delay
|
||||
pcmRX2Channel16 pcmRX = 8 << 0 // CH2WID (Arbitrary width between 8 and 16 is supported)
|
||||
)
|
||||
|
||||
type pcmTX uint32
|
||||
|
||||
// Page 133-134
|
||||
const (
|
||||
pcmTX1Width pcmTX = 1 << 31 // CH1WX Legacy
|
||||
pcmTX1Enable pcmTX = 1 << 30 // CH1EN Enable channel 1
|
||||
pcmTX1PosShift = 20
|
||||
pcmTX1PosMask pcmTX = 0x3F << pcmTX1PosShift // CH1POS Clock delay
|
||||
pcmTX1Channel16 pcmTX = 8 << 16 // CH1WID (Arbitrary width between 8 and 16 is supported)
|
||||
pcmTX2Width pcmTX = 1 << 15 // CH2WEX Legacy
|
||||
pcmTX2Enable pcmTX = 1 << 14 // CH2EN
|
||||
pcmTX2PosShift = 4
|
||||
pcmTX2PosMask pcmTX = 0x3F << pcmTX2PosShift // CH2POS Clock delay
|
||||
pcmTX2Channel16 pcmTX = 8 << 0 // CH2WID (Arbitrary width between 8 and 16 is supported)
|
||||
)
|
||||
|
||||
type pcmDreq uint32
|
||||
|
||||
// Page 134-135
|
||||
const (
|
||||
// 31 reserved
|
||||
pcmDreqTXPanicShift = 24
|
||||
pcmDreqTXPanicMask pcmDreq = 0x7F << pcmDreqTXPanicShift // TX_PANIC Panic level
|
||||
// 23 reserved
|
||||
pcmDreqRXPanicShift = 16
|
||||
pcmDreqRXPanicMask pcmDreq = 0x7F << pcmDreqRXPanicShift // RX_PANIC Panic level
|
||||
// 15 reserved
|
||||
pcmDreqTXLevelShift = 8
|
||||
pcmDreqTXLevelMask pcmDreq = 0x7F << pcmDreqTXPanicShift // TX Request Level
|
||||
// 7 reserved
|
||||
pcmDreqRXLevelShift = 0
|
||||
pcmDreqRXLevelMask pcmDreq = 0x7F << pcmDreqRXPanicShift // RX Request Level
|
||||
)
|
||||
|
||||
type pcmInterrupt uint32
|
||||
|
||||
// Page 135
|
||||
const (
|
||||
// 31:4 reserved
|
||||
pcmIntRXErr pcmInterrupt = 1 << 3 // RXERR RX error interrupt enable
|
||||
pcmIntTXErr pcmInterrupt = 1 << 2 // TXERR TX error interrupt enable
|
||||
pcmIntRXEnable pcmInterrupt = 1 << 1 // RXR RX Read interrupt enable
|
||||
pcmIntTXEnable pcmInterrupt = 1 << 0 // TXW TX Write interrupt enable
|
||||
)
|
||||
|
||||
type pcmIntStatus uint32
|
||||
|
||||
// Page 135-136
|
||||
const (
|
||||
// 31:4 reserved
|
||||
pcmIntStatRXErr pcmIntStatus = 1 << 3 // RXERR RX error occurred / clear
|
||||
pcmIntStatTXErr pcmIntStatus = 1 << 2 // TXERR TX error occurred / clear
|
||||
pcmIntStatRXEnable pcmIntStatus = 1 << 1 // RXR RX Read interrupt occurred / clear
|
||||
pcmIntStatTXEnable pcmIntStatus = 1 << 0 // TXW TX Write interrupt occurred / clear
|
||||
pcmIntStatusClear pcmIntStatus = 0xF
|
||||
)
|
||||
|
||||
// pcmGray puts it into a special data/strobe mode that is under 'best effort'
|
||||
// contract.
|
||||
type pcmGray uint32
|
||||
|
||||
// Page 136-137
|
||||
const (
|
||||
// 31:22 reserved
|
||||
pcmGrayRXFIFOLevelShift = 16
|
||||
pcmGrayRXFIFOLevelMask pcmGray = 0x3F << pcmGrayRXFIFOLevelShift // RXFIFOLEVEL How many words in RXFIFO
|
||||
pcmGrayFlushShift = 10
|
||||
pcmGrayFlushMask = 0x3F << pcmGrayFlushShift // FLUSHED How many bits were valid when flush occurred
|
||||
pcmGrayRXLevelShift = 4
|
||||
pcmGrayRXLevelMask pcmGray = 0x3F << pcmGrayRXLevelShift // RXLEVEL How many GRAY coded bits received
|
||||
pcmGrayFlush pcmGray = 1 << 2 // FLUSH
|
||||
pcmGrayClear pcmGray = 1 << 1 // CLR
|
||||
pcmGrayEnable pcmGray = 1 << 0 // EN
|
||||
)
|
||||
|
||||
// Page 119
|
||||
type pcmMap struct {
|
||||
cs pcmCS // CS_A Control Status
|
||||
fifo uint32 // FIFO_A FIFO register
|
||||
mode pcmMode // MODE_A Operation mode
|
||||
rxc pcmRX // RXC_A RX control
|
||||
txc pcmTX // TXC_A TX control
|
||||
dreq pcmDreq // DREQ_A DMA control
|
||||
inten pcmInterrupt // INTEN_A Interrupt enable
|
||||
intstc pcmIntStatus // INTSTC_A Interrupt status
|
||||
gray pcmGray // GRAY Gray mode input processing
|
||||
}
|
||||
|
||||
func (p *pcmMap) GoString() string {
|
||||
return fmt.Sprintf(
|
||||
"{\n cs: 0x%x,\n mode: 0x%x,\n rxc: 0x%x,\n txc: 0x%x,\n dreq: 0x%x,\n inten: 0x%x,\n intstc: 0x%x,\n gray: 0x%x,\n}",
|
||||
p.cs, p.mode, p.rxc, p.txc, p.dreq, p.inten, p.intstc, p.gray)
|
||||
}
|
||||
|
||||
func (p *pcmMap) reset() {
|
||||
p.cs = 0
|
||||
// In theory need to wait the equivalent of 2 PCM clocks.
|
||||
// TODO(maruel): Use pcmSync busy loop to synchronize.
|
||||
Nanospin(time.Microsecond)
|
||||
// Hard reset
|
||||
p.fifo = 0
|
||||
p.mode = 0
|
||||
p.rxc = 0
|
||||
p.txc = 0
|
||||
p.dreq = 0
|
||||
p.inten = 0
|
||||
p.intstc = pcmIntStatusClear
|
||||
p.gray = 0
|
||||
|
||||
// Clear pcmStandby / pcm
|
||||
}
|
||||
|
||||
// set initializes 8 bits stream via DMA with no delay and no FS.
|
||||
func (p *pcmMap) set() {
|
||||
p.cs |= pcmEnable
|
||||
p.txc = pcmTX1Width | pcmTX1Channel16 | pcmTX1Enable // 32bit TX
|
||||
p.mode = (32 - 1) << pcmFrameLengthShift
|
||||
p.cs |= pcmTXClear | pcmRXClear
|
||||
// In theory need to wait the equivalent of 2 PCM clocks.
|
||||
// TODO(maruel): Use pcmSync busy loop to synchronize.
|
||||
Nanospin(time.Microsecond)
|
||||
p.dreq = 0x10<<pcmDreqTXPanicShift | 0x30<<pcmDreqTXLevelShift
|
||||
p.cs |= pcmDMAEnable
|
||||
// pcmTXThresholdOne ?
|
||||
p.cs |= pcmTXEnable
|
||||
}
|
||||
|
||||
// setPCMClockSource sets the PCM clock.
|
||||
//
|
||||
// It may select an higher frequency than the one requested.
|
||||
//
|
||||
// Other potentially good clock sources are PWM, SPI and UART.
|
||||
func setPCMClockSource(f physic.Frequency) (physic.Frequency, uint32, error) {
|
||||
if drvDMA.pcmMemory == nil {
|
||||
return 0, 0, errors.New("subsystem PCM not initialized")
|
||||
}
|
||||
if drvDMA.clockMemory == nil {
|
||||
return 0, 0, errors.New("subsystem Clock not initialized")
|
||||
}
|
||||
actual, divs, err := drvDMA.clockMemory.pcm.set(f, 1)
|
||||
if err == nil {
|
||||
drvDMA.pcmMemory.cs = 0
|
||||
}
|
||||
// Convert divisor into wait cycles.
|
||||
return actual, divs, err
|
||||
}
|
272
vendor/periph.io/x/host/v3/bcm283x/pwm.go
generated
vendored
Normal file
272
vendor/periph.io/x/host/v3/bcm283x/pwm.go
generated
vendored
Normal file
@ -0,0 +1,272 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package bcm283x
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"periph.io/x/conn/v3/physic"
|
||||
)
|
||||
|
||||
// PWENi is used to enable/disable the corresponding channel. Setting this bit
|
||||
// to 1 enables the channel and transmitter state machine. All registers and
|
||||
// FIFO is writable without setting this bit.
|
||||
//
|
||||
// MODEi bit is used to determine mode of operation. Setting this bit to 0
|
||||
// enables PWM mode. In this mode data stored in either PWM_DATi or FIFO is
|
||||
// transmitted by pulse width modulation within the range defined by PWM_RNGi.
|
||||
// When this mode is used MSENi defines whether to use PWM algorithm. Setting
|
||||
// MODEi to 1 enables serial mode, in which data stored in either PWM_DATi or
|
||||
// FIFO is transmitted serially within the range defined by PWM_RNGi. Data is
|
||||
// transmitted MSB first and truncated or zeropadded depending on PWM_RNGi.
|
||||
// Default mode is PWM.
|
||||
//
|
||||
// RPTLi is used to enable/disable repeating of the last data available in the
|
||||
// FIFO just before it empties. When this bit is 1 and FIFO is used, the last
|
||||
// available data in the FIFO is repeatedly sent. This may be useful in PWM
|
||||
// mode to avoid duty cycle gaps. If the FIFO is not used this bit does not
|
||||
// have any effect. Default operation is do-notrepeat.
|
||||
//
|
||||
// SBITi defines the state of the output when no transmission takes place. It
|
||||
// also defines the zero polarity for the zero padding in serialiser mode. This
|
||||
// bit is padded between two consecutive transfers as well as tail of the data
|
||||
// when PWM_RNGi is larger than bit depth of data being transferred. this bit
|
||||
// is zero by default.
|
||||
//
|
||||
// POLAi is used to configure the polarity of the output bit. When set to high
|
||||
// the final output is inverted. Default operation is no inversion.
|
||||
//
|
||||
// USEFi bit is used to enable/disable FIFO transfer. When this bit is high
|
||||
// data stored in the FIFO is used for transmission. When it is low, data
|
||||
// written to PWM_DATi is transferred. This bit is 0 as default.
|
||||
//
|
||||
// CLRF is used to clear the FIFO. Writing a 1 to this bit clears the FIFO.
|
||||
// Writing 0 has no effect. This is a single shot operation and reading the bit
|
||||
// always returns 0.
|
||||
//
|
||||
// MSENi is used to determine whether to use PWM algorithm or simple M/S ratio
|
||||
// transmission. When this bit is high M/S transmission is used. This bit is
|
||||
// zero as default. When MODEi is 1, this configuration bit has no effect.
|
||||
//
|
||||
// See page 139-140 for the description of the PWM and M/S ratio algorithms.
|
||||
const (
|
||||
// 31:16 reserved
|
||||
pwm2MS pwmControl = 1 << 15 // MSEN2; 0: PWM algorithm is used; 1: M/S transmission is used
|
||||
// 14 reserved
|
||||
pwm2UseFIFO pwmControl = 1 << 13 // USEF2; 0: Data register is transmitted; 1: Fifo is used for transmission
|
||||
pwm2Polarity pwmControl = 1 << 12 // POLA2; 0: 0=low 1=high; 1: 1=low 0=high
|
||||
pwm2SilenceHigh pwmControl = 1 << 11 // SBIT2; Defines the state of the output when no transmission takes place
|
||||
pwm2RepeatLastData pwmControl = 1 << 10 // RPTL2; 0: Transmission interrupts when FIFO is empty; 1: Last data in FIFO is transmitted repetedly until FIFO is not empty
|
||||
pwm2Serialiser pwmControl = 1 << 9 // MODE2; 0: PWM mode; 1: Serialiser mode
|
||||
pwm2Enable pwmControl = 1 << 8 // PWEN2; Enable channel 2
|
||||
pwm2Mask pwmControl = pwm2MS | pwm2UseFIFO | pwm2Polarity | pwm2SilenceHigh | pwm2RepeatLastData | pwm2Serialiser | pwm2Enable
|
||||
pwm1MS pwmControl = 1 << 7 // MSEN1; 0: PWM algorithm is used; 1: M/S transmission is used
|
||||
pwmClearFIFO pwmControl = 1 << 6 // CLRF1; Clear the fifo
|
||||
pwm1UseFIFO pwmControl = 1 << 5 // USEF1; 0: Data register is transmitted; 1: Fifo is used for transmission
|
||||
pwm1Polarity pwmControl = 1 << 4 // POLA1; 0: 0=low 1=high; 1: 1=low 0=high
|
||||
pwm1SilenceHigh pwmControl = 1 << 3 // SBIT1; Defines the state of the output when no transmission takes place
|
||||
pwm1RepeatLastData pwmControl = 1 << 2 // RPTL1; 0: Transmission interrupts when FIFO is empty; 1: Last data in FIFO is transmitted repetedly until FIFO is not empty
|
||||
pwm1Serialiser pwmControl = 1 << 1 // MODE1; 0: PWM mode; 1: Serialiser mode
|
||||
pwm1Enable pwmControl = 1 << 0 // PWEN1; Enable channel 1
|
||||
pwm1Mask pwmControl = pwm1MS | pwm1UseFIFO | pwm1Polarity | pwm1SilenceHigh | pwm1RepeatLastData | pwm1Serialiser | pwm1Enable
|
||||
)
|
||||
|
||||
// Pages 141-143.
|
||||
type pwmControl uint32
|
||||
|
||||
const (
|
||||
// 31:13 reserved
|
||||
// STAi bit indicates the current state of the channel which is useful for
|
||||
// debugging purposes. The bit set means the channel is currently
|
||||
// transmitting data.
|
||||
pwmSta4 pwmStatus = 1 << 12 // STA4
|
||||
pwmSta3 pwmStatus = 1 << 11 // STA3
|
||||
pwmSta2 pwmStatus = 1 << 10 // STA2
|
||||
pwmSta1 pwmStatus = 1 << 9 // STA1
|
||||
// BERR sets to high when an error has occurred while writing to registers
|
||||
// via APB. This may happen if the bus tries to write successively to same
|
||||
// set of registers faster than the synchroniser block can cope with.
|
||||
// Multiple switching may occur and contaminate the data during
|
||||
// synchronisation. Software should clear this bit by writing 1. Writing 0
|
||||
// to this bit has no effect.
|
||||
pwmBusErr pwmStatus = 1 << 8 // BERR Bus Error flag
|
||||
// GAPOi. bit indicates that there has been a gap between transmission of two
|
||||
// consecutive data from FIFO. This may happen when FIFO gets empty after
|
||||
// state machine has sent a word and waits for the next. If control bit RPTLi
|
||||
// is set to high this event will not occur. Software must clear this bit by
|
||||
// writing 1. Writing 0 to this bit has no effect.
|
||||
pwmGapo4 pwmStatus = 1 << 7 // GAPO4 Channel 4 Gap Occurred flag
|
||||
pwmGapo3 pwmStatus = 1 << 6 // GAPO3 Channel 3 Gap Occurred flag
|
||||
pwmGapo2 pwmStatus = 1 << 5 // GAPO2 Channel 2 Gap Occurred flag
|
||||
pwmGapo1 pwmStatus = 1 << 4 // GAPO1 Channel 1 Gap Occurred flag
|
||||
// RERR1 bit sets to high when a read when empty error occurs. Software must
|
||||
// clear this bit by writing 1. Writing 0 to this bit has no effect.
|
||||
pwmRerr1 pwmStatus = 1 << 3 // RERR1
|
||||
// WERR1 bit sets to high when a write when full error occurs. Software must
|
||||
// clear this bit by writing 1. Writing 0 to this bit has no effect.
|
||||
pwmWerr1 pwmStatus = 1 << 2 // WERR1
|
||||
// EMPT1 bit indicates the empty status of the FIFO. If this bit is high FIFO
|
||||
// is empty.
|
||||
pwmEmpt1 pwmStatus = 1 << 1 // EMPT1
|
||||
// FULL1 bit indicates the full status of the FIFO. If this bit is high FIFO
|
||||
// is full.
|
||||
pwmFull1 pwmStatus = 1 << 0 // FULL1
|
||||
pwmStatusMask = pwmSta4 | pwmSta3 | pwmSta2 | pwmSta1 | pwmBusErr | pwmGapo4 | pwmGapo3 | pwmGapo2 | pwmGapo1 | pwmRerr1 | pwmWerr1 | pwmEmpt1 | pwmFull1
|
||||
)
|
||||
|
||||
// Pages 144-145.
|
||||
type pwmStatus uint32
|
||||
|
||||
const (
|
||||
pwmDMAEnable pwmDMACfg = 1 << 31 // ENAB
|
||||
// 30:16 reserved
|
||||
pwmPanicShift = 16
|
||||
pwmPanicMask pwmDMACfg = 0xFF << pwmPanicShift // PANIC Default is 7
|
||||
pwmDreqMask pwmDMACfg = 0xFF // DREQ Default is 7
|
||||
)
|
||||
|
||||
// Page 145.
|
||||
type pwmDMACfg uint32
|
||||
|
||||
// pwmMap is the block to control the PWM generator.
|
||||
//
|
||||
// Note that pins are named PWM0 and PWM1 but the mapping uses channel numbers
|
||||
// 1 and 2.
|
||||
// - PWM0: GPIO12, GPIO18, GPIO40, GPIO52.
|
||||
// - PWM1: GPIO13, GPIO19, GPIO41, GPIO45, GPIO53.
|
||||
//
|
||||
// Each channel works independently. They can either output a bitstream or a
|
||||
// serialised version of up to eight 32 bits words.
|
||||
//
|
||||
// The default base PWM frequency is 100Mhz.
|
||||
//
|
||||
// Description at page 138-139.
|
||||
//
|
||||
// Page 140-141.
|
||||
type pwmMap struct {
|
||||
ctl pwmControl // CTL
|
||||
status pwmStatus // STA
|
||||
dmaCfg pwmDMACfg // DMAC
|
||||
// This register is used to define the range for the corresponding channel.
|
||||
// In PWM mode evenly distributed pulses are sent within a period of length
|
||||
// defined by this register. In serial mode serialised data is transmitted
|
||||
// within the same period. If the value in PWM_RNGi is less than 32, only the
|
||||
// first PWM_RNGi bits are sent resulting in a truncation. If it is larger
|
||||
// than 32 excess zero bits are padded at the end of data. Default value for
|
||||
// this register is 32.
|
||||
dummy1 uint32 // Padding
|
||||
rng1 uint32 // RNG1
|
||||
// This register stores the 32 bit data to be sent by the PWM Controller when
|
||||
// USEFi is 0. In PWM mode data is sent by pulse width modulation: the value
|
||||
// of this register defines the number of pulses which is sent within the
|
||||
// period defined by PWM_RNGi. In serialiser mode data stored in this
|
||||
// register is serialised and transmitted.
|
||||
dat1 uint32 // DAT1
|
||||
// This register is the FIFO input for the all channels. Data written to this
|
||||
// address is stored in channel FIFO and if USEFi is enabled for the channel
|
||||
// i it is used as data to be sent. This register is write only, and reading
|
||||
// this register will always return bus default return value, pwm0.
|
||||
// When more than one channel is enabled for FIFO usage, the data written
|
||||
// into the FIFO is shared between these channels in turn. For example if the
|
||||
// word series A B C D E F G H I .. is written to FIFO and two channels are
|
||||
// active and configured to use FIFO then channel 1 will transmit words A C E
|
||||
// G I .. and channel 2 will transmit words B D F H .. . Note that
|
||||
// requesting data from the FIFO is in locked-step manner and therefore
|
||||
// requires tight coupling of state machines of the channels. If any of the
|
||||
// channel range (period) value is different than the others this will cause
|
||||
// the channels with small range values to wait between words hence resulting
|
||||
// in gaps between words. To avoid that, each channel sharing the FIFO should
|
||||
// be configured to use the same range value. Also note that RPTLi are not
|
||||
// meaningful when the FIFO is shared between channels as there is no defined
|
||||
// channel to own the last data in the FIFO. Therefore sharing channels must
|
||||
// have their RPTLi set to zero.
|
||||
//
|
||||
// If the set of channels to share the FIFO has been modified after a
|
||||
// configuration change, FIFO should be cleared before writing new data.
|
||||
fifo uint32 // FIF1
|
||||
dummy2 uint32 // Padding
|
||||
rng2 uint32 // RNG2 Equivalent of rng1 for channel 2
|
||||
dat2 uint32 // DAT2 Equivalent of dat1 for channel 2
|
||||
}
|
||||
|
||||
// reset stops the PWM.
|
||||
func (p *pwmMap) reset() {
|
||||
p.dmaCfg = 0
|
||||
p.ctl |= pwmClearFIFO
|
||||
p.ctl &^= pwm1Enable | pwm2Enable
|
||||
Nanospin(100 * time.Microsecond) // Cargo cult copied. Probably not necessary.
|
||||
p.status = pwmBusErr | pwmGapo1 | pwmGapo2 | pwmGapo3 | pwmGapo4 | pwmRerr1 | pwmWerr1
|
||||
Nanospin(100 * time.Microsecond)
|
||||
// Use the full 32 bits of DATi.
|
||||
p.rng1 = 32
|
||||
p.rng2 = 32
|
||||
}
|
||||
|
||||
// setPWMClockSource sets the PWM clock for use by the DMA controller for
|
||||
// pacing.
|
||||
//
|
||||
// It may select an higher frequency than the one requested.
|
||||
//
|
||||
// Other potentially good clock sources are PCM, SPI and UART.
|
||||
func setPWMClockSource() (physic.Frequency, error) {
|
||||
if drvDMA.pwmMemory == nil {
|
||||
return 0, errors.New("subsystem PWM not initialized")
|
||||
}
|
||||
if drvDMA.clockMemory == nil {
|
||||
return 0, errors.New("subsystem Clock not initialized")
|
||||
}
|
||||
if drvDMA.pwmDMACh != nil {
|
||||
// Already initialized
|
||||
return drvDMA.pwmDMAFreq, nil
|
||||
}
|
||||
|
||||
// divs * div must fit in rng1 registor.
|
||||
div := uint32(drvDMA.pwmBaseFreq / drvDMA.pwmDMAFreq)
|
||||
actual, divs, err := drvDMA.clockMemory.pwm.set(drvDMA.pwmBaseFreq, div)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if e := actual / physic.Frequency(divs*div); drvDMA.pwmDMAFreq != e {
|
||||
return 0, fmt.Errorf("unexpected DMA frequency %s != %s (%d/%d/%d)", drvDMA.pwmDMAFreq, e, actual, divs, div)
|
||||
}
|
||||
// It acts as a clock multiplier, since this amount of data is sent per
|
||||
// clock tick.
|
||||
drvDMA.pwmMemory.rng1 = divs * div
|
||||
Nanospin(10 * time.Microsecond)
|
||||
// Periph data (?)
|
||||
|
||||
// Use low priority.
|
||||
drvDMA.pwmMemory.dmaCfg = pwmDMAEnable | pwmDMACfg(15<<pwmPanicShift|15)
|
||||
Nanospin(10 * time.Microsecond)
|
||||
drvDMA.pwmMemory.ctl |= pwmClearFIFO
|
||||
Nanospin(10 * time.Microsecond)
|
||||
old := drvDMA.pwmMemory.ctl
|
||||
drvDMA.pwmMemory.ctl = (old & ^pwmControl(0xff)) | pwm1UseFIFO | pwm1Enable
|
||||
|
||||
// Start DMA
|
||||
if drvDMA.pwmDMACh, drvDMA.pwmDMABuf, err = dmaWritePWMFIFO(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return drvDMA.pwmDMAFreq, nil
|
||||
}
|
||||
|
||||
func resetPWMClockSource() error {
|
||||
if drvDMA.pwmDMACh != nil {
|
||||
drvDMA.pwmDMACh.reset()
|
||||
drvDMA.pwmDMACh = nil
|
||||
}
|
||||
if drvDMA.pwmDMABuf != nil {
|
||||
if err := drvDMA.pwmDMABuf.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
drvDMA.pwmDMABuf = nil
|
||||
}
|
||||
_, _, err := drvDMA.clockMemory.pwm.set(0, 0)
|
||||
return err
|
||||
}
|
134
vendor/periph.io/x/host/v3/bcm283x/streams.go
generated
vendored
Normal file
134
vendor/periph.io/x/host/v3/bcm283x/streams.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package bcm283x
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"periph.io/x/conn/v3/gpio/gpiostream"
|
||||
)
|
||||
|
||||
// uint32ToBitLSBF packs a bit offset found on slice `d` (that is actually
|
||||
// uint32) back into a densely packed Bits stream.
|
||||
func uint32ToBitLSBF(w []byte, d []uint8, bit uint8, skip int) {
|
||||
// Little endian.
|
||||
x := bit / 8
|
||||
d = d[x:]
|
||||
bit -= 8 * x
|
||||
mask := uint8(1) << bit
|
||||
for i := range w {
|
||||
w[i] = ((d[0]&mask)>>bit<<0 |
|
||||
(d[skip*1]&mask)>>bit<<1 |
|
||||
(d[skip*2]&mask)>>bit<<2 |
|
||||
(d[skip*3]&mask)>>bit<<3 |
|
||||
(d[skip*4]&mask)>>bit<<4 |
|
||||
(d[skip*5]&mask)>>bit<<5 |
|
||||
(d[skip*6]&mask)>>bit<<6 |
|
||||
(d[skip*7]&mask)>>bit<<7)
|
||||
d = d[skip*8:]
|
||||
}
|
||||
}
|
||||
|
||||
func getBit(b byte, index int, msb bool) byte {
|
||||
var shift uint
|
||||
if msb {
|
||||
shift = uint(7 - index)
|
||||
} else {
|
||||
shift = uint(index)
|
||||
}
|
||||
return (b >> shift) & 1
|
||||
}
|
||||
|
||||
func raster32Bits(s gpiostream.Stream, skip int, clear, set []uint32, mask uint32) error {
|
||||
var msb bool
|
||||
var bits []byte
|
||||
switch b := s.(type) {
|
||||
case *gpiostream.BitStream:
|
||||
msb = !b.LSBF
|
||||
bits = b.Bits
|
||||
default:
|
||||
return fmt.Errorf("unsupported type %T", b)
|
||||
}
|
||||
m := len(clear) / 8
|
||||
if n := len(bits); n < m {
|
||||
m = n
|
||||
}
|
||||
index := 0
|
||||
for i := 0; i < m; i++ {
|
||||
for j := 0; j < 8; j++ {
|
||||
if getBit(bits[i], j, msb) != 0 {
|
||||
for k := 0; k < skip; k++ {
|
||||
set[index] |= mask
|
||||
index++
|
||||
}
|
||||
} else {
|
||||
for k := 0; k < skip; k++ {
|
||||
clear[index] |= mask
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// raster32 rasters the stream into a uint32 stream with the specified masks to
|
||||
// put in the correctly slice when the bit is set and when it is clear.
|
||||
//
|
||||
// `s` must be one of the types in this package.
|
||||
func raster32(s gpiostream.Stream, skip int, clear, set []uint32, mask uint32) error {
|
||||
if mask == 0 {
|
||||
return errors.New("bcm283x: mask is 0")
|
||||
}
|
||||
if len(clear) == 0 {
|
||||
return errors.New("bcm283x: clear buffer is empty")
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return errors.New("bcm283x: set buffer is empty")
|
||||
}
|
||||
if len(clear) != len(set) {
|
||||
return errors.New("bcm283x: clear and set buffers have different length")
|
||||
}
|
||||
switch x := s.(type) {
|
||||
case *gpiostream.BitStream:
|
||||
// TODO
|
||||
return raster32Bits(x, skip, clear, set, mask)
|
||||
case *gpiostream.EdgeStream:
|
||||
return errors.New("bcm283x: EdgeStream is not supported yet")
|
||||
case *gpiostream.Program:
|
||||
return errors.New("bcm283x: Program is not supported yet")
|
||||
default:
|
||||
return errors.New("bcm283x: unknown stream type")
|
||||
}
|
||||
}
|
||||
|
||||
// PCM/PWM DMA buf is encoded as little-endian and MSB first.
|
||||
func copyStreamToDMABuf(w gpiostream.Stream, dst []uint32) error {
|
||||
switch v := w.(type) {
|
||||
case *gpiostream.BitStream:
|
||||
if v.LSBF {
|
||||
return errors.New("TODO(simokawa): handle BitStream.LSBF")
|
||||
}
|
||||
// This is big-endian and MSB first.
|
||||
i := 0
|
||||
for ; i < len(v.Bits)/4; i++ {
|
||||
dst[i] = binary.BigEndian.Uint32(v.Bits[i*4:])
|
||||
}
|
||||
last := uint32(0)
|
||||
if mod := len(v.Bits) % 4; mod > 0 {
|
||||
for j := 0; j < mod; j++ {
|
||||
last |= (uint32(v.Bits[i*4+j])) << uint32(8*(3-j))
|
||||
}
|
||||
dst[i] = last
|
||||
}
|
||||
return nil
|
||||
case *gpiostream.EdgeStream:
|
||||
return errors.New("TODO(simokawa): handle EdgeStream")
|
||||
default:
|
||||
return errors.New("unsupported Stream type")
|
||||
}
|
||||
}
|
60
vendor/periph.io/x/host/v3/bcm283x/timer.go
generated
vendored
Normal file
60
vendor/periph.io/x/host/v3/bcm283x/timer.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package bcm283x
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"periph.io/x/host/v3/cpu"
|
||||
)
|
||||
|
||||
// ReadTime returns the time on a monotonic 1Mhz clock (1µs resolution).
|
||||
//
|
||||
// It only works if bcm283x-dma successfully loaded. Otherwise it returns 0.
|
||||
func ReadTime() time.Duration {
|
||||
if drvDMA.timerMemory == nil {
|
||||
return 0
|
||||
}
|
||||
return (time.Duration(drvDMA.timerMemory.high)<<32 | time.Duration(drvDMA.timerMemory.low)) * time.Microsecond
|
||||
}
|
||||
|
||||
// Nanospin spins the CPU without calling into the kernel code if possible.
|
||||
func Nanospin(t time.Duration) {
|
||||
start := ReadTime()
|
||||
if start == 0 {
|
||||
// Use the slow generic version.
|
||||
cpu.Nanospin(t)
|
||||
return
|
||||
}
|
||||
// TODO(maruel): Optimize code path for sub-1µs duration.
|
||||
for ReadTime()-start < t {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const (
|
||||
// 31:4 reserved
|
||||
timerM3 = 1 << 3 // M3
|
||||
timerM2 = 1 << 2 // M2
|
||||
timerM1 = 1 << 1 // M1
|
||||
timerM0 = 1 << 0 // M0
|
||||
)
|
||||
|
||||
// Page 173
|
||||
type timerCtl uint32
|
||||
|
||||
// timerMap represents the registers to access the 1Mhz timer.
|
||||
//
|
||||
// Page 172
|
||||
type timerMap struct {
|
||||
ctl timerCtl // CS
|
||||
low uint32 // CLO
|
||||
high uint32 // CHI
|
||||
c0 uint32 // 0
|
||||
c1 uint32 // C1
|
||||
c2 uint32 // C2
|
||||
c3 uint32 // C3
|
||||
}
|
34
vendor/periph.io/x/host/v3/beagle/black/black.go
generated
vendored
Normal file
34
vendor/periph.io/x/host/v3/beagle/black/black.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package black implements headers for the BeagleBone Black and BeagleBone
|
||||
// Black Wireless micro-computers.
|
||||
//
|
||||
// Reference
|
||||
//
|
||||
// https://beagleboard.org/black
|
||||
//
|
||||
// Datasheet
|
||||
//
|
||||
// https://elinux.org/Beagleboard:BeagleBoneBlack
|
||||
//
|
||||
// https://github.com/CircuitCo/BeagleBone-Black/blob/rev_b/BBB_SRM.pdf
|
||||
//
|
||||
// https://elinux.org/Beagleboard:Cape_Expansion_Headers
|
||||
package black
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"periph.io/x/host/v3/distro"
|
||||
)
|
||||
|
||||
// Present returns true if the host is a BeagleBone Black or BeagleBone Black
|
||||
// Wireless.
|
||||
func Present() bool {
|
||||
if isArm {
|
||||
return strings.HasPrefix(distro.DTModel(), "TI AM335x BeagleBone Black")
|
||||
}
|
||||
return false
|
||||
}
|
7
vendor/periph.io/x/host/v3/beagle/black/black_arm.go
generated
vendored
Normal file
7
vendor/periph.io/x/host/v3/beagle/black/black_arm.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package black
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/beagle/black/black_other.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/beagle/black/black_other.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm
|
||||
|
||||
package black
|
||||
|
||||
const isArm = false
|
318
vendor/periph.io/x/host/v3/beagle/bone/bone.go
generated
vendored
Normal file
318
vendor/periph.io/x/host/v3/beagle/bone/bone.go
generated
vendored
Normal file
@ -0,0 +1,318 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package bone implements headers J1, P8 and P9 found on many (but not all)
|
||||
// BeagleBone micro-computer.
|
||||
//
|
||||
// In particular, the headers are found on the models using a TI AM335x
|
||||
// processor: BeagleBone Black, Black Wireless, Green and Green Wireless.
|
||||
//
|
||||
// Reference
|
||||
//
|
||||
// http://beagleboard.org/Support/bone101/#hardware
|
||||
package bone
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"periph.io/x/conn/v3/driver/driverreg"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/pin"
|
||||
"periph.io/x/conn/v3/pin/pinreg"
|
||||
"periph.io/x/host/v3/beagle/black"
|
||||
"periph.io/x/host/v3/beagle/green"
|
||||
"periph.io/x/host/v3/sysfs"
|
||||
)
|
||||
|
||||
// TODO(maruel): Use specialized am335x or pru implementation once available.
|
||||
|
||||
// Common pin types on BeagleBones.
|
||||
var (
|
||||
PWR_BUT = &pin.BasicPin{N: "PWR_BUT"} //
|
||||
RESET_OUT = &pin.BasicPin{N: "RESET_OUT"} // SYS_RESETn
|
||||
VADC = &pin.BasicPin{N: "VADC"} // VDD_ADC
|
||||
AIN4 = &pin.BasicPin{N: "AIN4"} // AIN4
|
||||
AGND = &pin.BasicPin{N: "AGND"} // GNDA_ADC
|
||||
AIN6 = &pin.BasicPin{N: "AIN6"} // AIN6
|
||||
AIN5 = &pin.BasicPin{N: "AIN5"} // AIN5
|
||||
AIN2 = &pin.BasicPin{N: "AIN2"} // AIN2
|
||||
AIN3 = &pin.BasicPin{N: "AIN3"} // AIN3
|
||||
AIN0 = &pin.BasicPin{N: "AIN0"} // AIN0
|
||||
AIN1 = &pin.BasicPin{N: "AIN1"} // AIN1
|
||||
)
|
||||
|
||||
// Headers found on BeagleBones.
|
||||
var (
|
||||
// Port J1 is the UART port where the default terminal is connected to.
|
||||
J1_1 pin.Pin = pin.GROUND
|
||||
J1_2 pin.Pin = pin.INVALID
|
||||
J1_3 pin.Pin = pin.INVALID
|
||||
J1_4 gpio.PinIO = gpio.INVALID // GPIO42, UART0_RX
|
||||
J1_5 gpio.PinIO = gpio.INVALID // GPIO43, UART0_TX
|
||||
J1_6 pin.Pin = pin.INVALID
|
||||
|
||||
P8_1 pin.Pin = pin.GROUND
|
||||
P8_2 pin.Pin = pin.GROUND
|
||||
P8_3 gpio.PinIO = gpio.INVALID // GPIO38, MMC1_DAT6
|
||||
P8_4 gpio.PinIO = gpio.INVALID // GPIO39, MMC1_DAT7
|
||||
P8_5 gpio.PinIO = gpio.INVALID // GPIO34, MMC1_DAT2
|
||||
P8_6 gpio.PinIO = gpio.INVALID // GPIO35, MMC1_DAT3
|
||||
P8_7 gpio.PinIO = gpio.INVALID // GPIO66, Timer4
|
||||
P8_8 gpio.PinIO = gpio.INVALID // GPIO67, Timer7
|
||||
P8_9 gpio.PinIO = gpio.INVALID // GPIO69, Timer5
|
||||
P8_10 gpio.PinIO = gpio.INVALID // GPIO68, Timer6
|
||||
P8_11 gpio.PinIO = gpio.INVALID // GPIO45,
|
||||
P8_12 gpio.PinIO = gpio.INVALID // GPIO44,
|
||||
P8_13 gpio.PinIO = gpio.INVALID // GPIO23, EHRPWM2B
|
||||
P8_14 gpio.PinIO = gpio.INVALID // GPIO26,
|
||||
P8_15 gpio.PinIO = gpio.INVALID // GPIO47,
|
||||
P8_16 gpio.PinIO = gpio.INVALID // GPIO46,
|
||||
P8_17 gpio.PinIO = gpio.INVALID // GPIO27,
|
||||
P8_18 gpio.PinIO = gpio.INVALID // GPIO65,
|
||||
P8_19 gpio.PinIO = gpio.INVALID // GPIO22, EHRPWM2A
|
||||
P8_20 gpio.PinIO = gpio.INVALID // GPIO63, MMC1_CMD
|
||||
P8_21 gpio.PinIO = gpio.INVALID // GPIO62, MMC1_CLK
|
||||
P8_22 gpio.PinIO = gpio.INVALID // GPIO37, MMC1_DAT5
|
||||
P8_23 gpio.PinIO = gpio.INVALID // GPIO36, MMC1_DAT4
|
||||
P8_24 gpio.PinIO = gpio.INVALID // GPIO33, MMC1_DAT1
|
||||
P8_25 gpio.PinIO = gpio.INVALID // GPIO32, MMC1_DAT0
|
||||
P8_26 gpio.PinIO = gpio.INVALID // GPIO61,
|
||||
P8_27 gpio.PinIO = gpio.INVALID // GPIO86, LCD_VSYNC
|
||||
P8_28 gpio.PinIO = gpio.INVALID // GPIO88, LCD_PCLK
|
||||
P8_29 gpio.PinIO = gpio.INVALID // GPIO87, LCD_HSYNC
|
||||
P8_30 gpio.PinIO = gpio.INVALID // GPIO89, LCD_AC_BIAS_E
|
||||
P8_31 gpio.PinIO = gpio.INVALID // GPIO10, LCD_DATA14, UART4_CTS
|
||||
P8_32 gpio.PinIO = gpio.INVALID // GPIO11, LCD_DATA15, UART5_RTS
|
||||
P8_33 gpio.PinIO = gpio.INVALID // GPIO9, LCD_DATA13, UART4_RTS
|
||||
P8_34 gpio.PinIO = gpio.INVALID // GPIO81, LCD_DATA11, EHRPWM1B, UART3_RTS
|
||||
P8_35 gpio.PinIO = gpio.INVALID // GPIO8, LCD_DATA12, UART4_CTS
|
||||
P8_36 gpio.PinIO = gpio.INVALID // GPIO80, LCD_DATA10, EHRPWM1A, UART3_CTS
|
||||
P8_37 gpio.PinIO = gpio.INVALID // GPIO78, LCD_DATA8, UART5_TX
|
||||
P8_38 gpio.PinIO = gpio.INVALID // GPIO79, LCD_DATA9, UART5_RX
|
||||
P8_39 gpio.PinIO = gpio.INVALID // GPIO76, LCD_DATA6
|
||||
P8_40 gpio.PinIO = gpio.INVALID // GPIO77, LCD_DATA7
|
||||
P8_41 gpio.PinIO = gpio.INVALID // GPIO74, LCD_DATA4
|
||||
P8_42 gpio.PinIO = gpio.INVALID // GPIO75, LCD_DATA5
|
||||
P8_43 gpio.PinIO = gpio.INVALID // GPIO72, LCD_DATA2
|
||||
P8_44 gpio.PinIO = gpio.INVALID // GPIO73, LCD_DATA3
|
||||
P8_45 gpio.PinIO = gpio.INVALID // GPIO70, LCD_DATA0, EHRPWM2A
|
||||
P8_46 gpio.PinIO = gpio.INVALID // GPIO71, LCD_DATA1, EHRPWM2B
|
||||
|
||||
P9_1 pin.Pin = pin.GROUND
|
||||
P9_2 pin.Pin = pin.GROUND
|
||||
P9_3 pin.Pin = pin.V3_3
|
||||
P9_4 pin.Pin = pin.V3_3
|
||||
P9_5 pin.Pin = pin.V5
|
||||
P9_6 pin.Pin = pin.V5
|
||||
P9_7 pin.Pin = pin.V5
|
||||
P9_8 pin.Pin = pin.V5
|
||||
P9_9 pin.Pin = PWR_BUT // PWR_BUT
|
||||
P9_10 pin.Pin = RESET_OUT // SYS_RESETn
|
||||
P9_11 gpio.PinIO = gpio.INVALID // GPIO30, UART4_RX
|
||||
P9_12 gpio.PinIO = gpio.INVALID // GPIO60
|
||||
P9_13 gpio.PinIO = gpio.INVALID // GPIO31, UART4_TX
|
||||
P9_14 gpio.PinIO = gpio.INVALID // GPIO50, EHRPWM1A
|
||||
P9_15 gpio.PinIO = gpio.INVALID // GPIO48
|
||||
P9_16 gpio.PinIO = gpio.INVALID // GPIO51, EHRPWM1B
|
||||
P9_17 gpio.PinIO = gpio.INVALID // GPIO5, I2C1_SCL, SPI0_CS0
|
||||
P9_18 gpio.PinIO = gpio.INVALID // GPIO4, I2C1_SDA, SPI0_MISO
|
||||
P9_19 gpio.PinIO = gpio.INVALID // GPIO13, I2C2_SCL, UART1_RTS, SPI1_CS1
|
||||
P9_20 gpio.PinIO = gpio.INVALID // GPIO12, I2C2_SDA, UART1_CTS, SPI1_CS0
|
||||
P9_21 gpio.PinIO = gpio.INVALID // GPIO3, EHRPWM0B, I2C2_SCL, UART2_TX, SPI0_MOSI
|
||||
P9_22 gpio.PinIO = gpio.INVALID // GPIO2, EHRPWM0A, I2C2_SDA, UART2_RX, SPI0_CLK
|
||||
P9_23 gpio.PinIO = gpio.INVALID // GPIO49
|
||||
P9_24 gpio.PinIO = gpio.INVALID // GPIO15, I2C1_SCL, UART1_TX
|
||||
P9_25 gpio.PinIO = gpio.INVALID // GPIO117
|
||||
P9_26 gpio.PinIO = gpio.INVALID // GPIO14, I2C1_SDA, UART1_RX
|
||||
P9_27 gpio.PinIO = gpio.INVALID // GPIO115
|
||||
P9_28 gpio.PinIO = gpio.INVALID // GPIO113, ECAPPWM2, SPI1_CS0
|
||||
P9_29 gpio.PinIO = gpio.INVALID // GPIO111, EHRPWM0B, SPI1_MOSI
|
||||
P9_30 gpio.PinIO = gpio.INVALID // GPIO112, SPI1_MISO
|
||||
P9_31 gpio.PinIO = gpio.INVALID // GPIO110, EHRPWM0A, SPI1_CLK
|
||||
P9_32 pin.Pin = VADC // VDD_ADC
|
||||
P9_33 pin.Pin = AIN4 // AIN4
|
||||
P9_34 pin.Pin = AGND // GNDA_ADC
|
||||
P9_35 pin.Pin = AIN6 // AIN6
|
||||
P9_36 pin.Pin = AIN5 // AIN5
|
||||
P9_37 pin.Pin = AIN2 // AIN2
|
||||
P9_38 pin.Pin = AIN3 // AIN3
|
||||
P9_39 pin.Pin = AIN0 // AIN0
|
||||
P9_40 pin.Pin = AIN1 // AIN1
|
||||
P9_41 gpio.PinIO = gpio.INVALID // GPIO20
|
||||
P9_42 gpio.PinIO = gpio.INVALID // GPIO7, ECAPPWM0, UART3_TX, SPI1_CS1
|
||||
P9_43 pin.Pin = pin.GROUND
|
||||
P9_44 pin.Pin = pin.GROUND
|
||||
P9_45 pin.Pin = pin.GROUND
|
||||
P9_46 pin.Pin = pin.GROUND
|
||||
)
|
||||
|
||||
// Present returns true if the host is a BeagleBone Black/Green or their
|
||||
// Wireless version.
|
||||
func Present() bool {
|
||||
return black.Present() || green.Present()
|
||||
}
|
||||
|
||||
// driver implements periph.Driver.
|
||||
type driver struct {
|
||||
}
|
||||
|
||||
func (d *driver) String() string {
|
||||
return "beaglebone"
|
||||
}
|
||||
|
||||
func (d *driver) Prerequisites() []string {
|
||||
return []string{"am335x", "sysfs-gpio"}
|
||||
}
|
||||
|
||||
func (d *driver) After() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) Init() (bool, error) {
|
||||
if !Present() {
|
||||
return false, errors.New("BeagleBone board not detected")
|
||||
}
|
||||
|
||||
J1_4 = sysfs.Pins[42]
|
||||
J1_5 = sysfs.Pins[43]
|
||||
|
||||
P8_3 = sysfs.Pins[38]
|
||||
P8_4 = sysfs.Pins[39]
|
||||
P8_5 = sysfs.Pins[34]
|
||||
P8_6 = sysfs.Pins[35]
|
||||
P8_7 = sysfs.Pins[66]
|
||||
P8_8 = sysfs.Pins[67]
|
||||
P8_9 = sysfs.Pins[69]
|
||||
P8_10 = sysfs.Pins[68]
|
||||
P8_11 = sysfs.Pins[45]
|
||||
P8_12 = sysfs.Pins[44]
|
||||
P8_13 = sysfs.Pins[23]
|
||||
P8_14 = sysfs.Pins[26]
|
||||
P8_15 = sysfs.Pins[47]
|
||||
P8_16 = sysfs.Pins[46]
|
||||
P8_17 = sysfs.Pins[27]
|
||||
P8_18 = sysfs.Pins[65]
|
||||
P8_19 = sysfs.Pins[22]
|
||||
P8_20 = sysfs.Pins[63]
|
||||
P8_21 = sysfs.Pins[62]
|
||||
P8_22 = sysfs.Pins[37]
|
||||
P8_23 = sysfs.Pins[36]
|
||||
P8_24 = sysfs.Pins[33]
|
||||
P8_25 = sysfs.Pins[32]
|
||||
P8_26 = sysfs.Pins[61]
|
||||
P8_27 = sysfs.Pins[86]
|
||||
P8_28 = sysfs.Pins[88]
|
||||
P8_29 = sysfs.Pins[87]
|
||||
P8_30 = sysfs.Pins[89]
|
||||
P8_31 = sysfs.Pins[10]
|
||||
P8_32 = sysfs.Pins[11]
|
||||
P8_33 = sysfs.Pins[9]
|
||||
P8_34 = sysfs.Pins[81]
|
||||
P8_35 = sysfs.Pins[8]
|
||||
P8_36 = sysfs.Pins[80]
|
||||
P8_37 = sysfs.Pins[78]
|
||||
P8_38 = sysfs.Pins[79]
|
||||
P8_39 = sysfs.Pins[76]
|
||||
P8_40 = sysfs.Pins[77]
|
||||
P8_41 = sysfs.Pins[74]
|
||||
P8_42 = sysfs.Pins[75]
|
||||
P8_43 = sysfs.Pins[72]
|
||||
P8_44 = sysfs.Pins[73]
|
||||
P8_45 = sysfs.Pins[70]
|
||||
P8_46 = sysfs.Pins[71]
|
||||
|
||||
P9_11 = sysfs.Pins[30]
|
||||
P9_12 = sysfs.Pins[60]
|
||||
P9_13 = sysfs.Pins[31]
|
||||
P9_14 = sysfs.Pins[50]
|
||||
P9_15 = sysfs.Pins[48]
|
||||
P9_16 = sysfs.Pins[51]
|
||||
P9_17 = sysfs.Pins[5]
|
||||
P9_18 = sysfs.Pins[4]
|
||||
P9_19 = sysfs.Pins[13]
|
||||
P9_20 = sysfs.Pins[12]
|
||||
P9_21 = sysfs.Pins[3]
|
||||
P9_22 = sysfs.Pins[2]
|
||||
P9_23 = sysfs.Pins[49]
|
||||
P9_24 = sysfs.Pins[15]
|
||||
P9_25 = sysfs.Pins[117]
|
||||
P9_26 = sysfs.Pins[14]
|
||||
P9_27 = sysfs.Pins[115]
|
||||
P9_28 = sysfs.Pins[113]
|
||||
P9_29 = sysfs.Pins[111]
|
||||
P9_30 = sysfs.Pins[112]
|
||||
P9_31 = sysfs.Pins[110]
|
||||
P9_41 = sysfs.Pins[20]
|
||||
P9_42 = sysfs.Pins[7]
|
||||
|
||||
hdr := [][]pin.Pin{{J1_1}, {J1_2}, {J1_3}, {J1_4}, {J1_5}, {J1_6}}
|
||||
if err := pinreg.Register("J1", hdr); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
hdr = [][]pin.Pin{
|
||||
{P8_1, P8_2},
|
||||
{P8_3, P8_4},
|
||||
{P8_5, P8_6},
|
||||
{P8_7, P8_8},
|
||||
{P8_9, P8_10},
|
||||
{P8_11, P8_12},
|
||||
{P8_13, P8_14},
|
||||
{P8_15, P8_16},
|
||||
{P8_17, P8_18},
|
||||
{P8_19, P8_20},
|
||||
{P8_21, P8_22},
|
||||
{P8_23, P8_24},
|
||||
{P8_25, P8_26},
|
||||
{P8_27, P8_28},
|
||||
{P8_29, P8_30},
|
||||
{P8_31, P8_32},
|
||||
{P8_33, P8_34},
|
||||
{P8_35, P8_36},
|
||||
{P8_37, P8_38},
|
||||
{P8_39, P8_40},
|
||||
{P8_41, P8_42},
|
||||
{P8_43, P8_44},
|
||||
{P8_45, P8_46},
|
||||
}
|
||||
if err := pinreg.Register("P8", hdr); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
hdr = [][]pin.Pin{
|
||||
{P9_1, P9_2},
|
||||
{P9_3, P9_4},
|
||||
{P9_5, P9_6},
|
||||
{P9_7, P9_8},
|
||||
{P9_9, P9_10},
|
||||
{P9_11, P9_12},
|
||||
{P9_13, P9_14},
|
||||
{P9_15, P9_16},
|
||||
{P9_17, P9_18},
|
||||
{P9_19, P9_20},
|
||||
{P9_21, P9_22},
|
||||
{P9_23, P9_24},
|
||||
{P9_25, P9_26},
|
||||
{P9_27, P9_28},
|
||||
{P9_29, P9_30},
|
||||
{P9_31, P9_32},
|
||||
{P9_33, P9_34},
|
||||
{P9_35, P9_36},
|
||||
{P9_37, P9_38},
|
||||
{P9_39, P9_40},
|
||||
{P9_41, P9_42},
|
||||
{P9_43, P9_44},
|
||||
{P9_45, P9_46},
|
||||
}
|
||||
err := pinreg.Register("P9", hdr)
|
||||
return true, err
|
||||
}
|
||||
|
||||
func init() {
|
||||
if isArm {
|
||||
driverreg.MustRegister(&drv)
|
||||
}
|
||||
}
|
||||
|
||||
var drv driver
|
7
vendor/periph.io/x/host/v3/beagle/bone/bone_arm.go
generated
vendored
Normal file
7
vendor/periph.io/x/host/v3/beagle/bone/bone_arm.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package bone
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/beagle/bone/bone_other.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/beagle/bone/bone_other.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm
|
||||
|
||||
package bone
|
||||
|
||||
const isArm = false
|
95
vendor/periph.io/x/host/v3/beagle/green/green.go
generated
vendored
Normal file
95
vendor/periph.io/x/host/v3/beagle/green/green.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package green implements headers for the BeagleBone Green and BeagleBone
|
||||
// Green Wireless micro-computers.
|
||||
//
|
||||
// Reference
|
||||
//
|
||||
// https://beagleboard.org/green
|
||||
//
|
||||
// https://beagleboard.org/green-wireless
|
||||
//
|
||||
// Datasheet
|
||||
//
|
||||
// http://wiki.seeedstudio.com/BeagleBone_Green/
|
||||
package green
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"periph.io/x/conn/v3/driver/driverreg"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/pin"
|
||||
"periph.io/x/conn/v3/pin/pinreg"
|
||||
"periph.io/x/host/v3/distro"
|
||||
"periph.io/x/host/v3/sysfs"
|
||||
)
|
||||
|
||||
// Headers found on BeagleBone Green.
|
||||
var (
|
||||
// I2C Groove port.
|
||||
I2C_SCL gpio.PinIO = gpio.INVALID // GPIO13, I2C2_SCL, UART1_RTS, SPI1_CS1
|
||||
I2C_SDA gpio.PinIO = gpio.INVALID // GPIO12, I2C2_SDA, UART1_CTS, SPI1_CS0
|
||||
|
||||
// UART Groove port connected to UART2.
|
||||
UART_TX gpio.PinIO = gpio.INVALID // GPIO3, EHRPWM0B, I2C2_SCL, UART2_TX, SPI0_MISO
|
||||
UART_RX gpio.PinIO = gpio.INVALID // GPIO2, EHRPWM0A, I2C2_SDA, UART2_RX, SPI0_CLK
|
||||
)
|
||||
|
||||
// Present returns true if the host is a BeagleBone Green or BeagleBone Green
|
||||
// Wireless.
|
||||
func Present() bool {
|
||||
if isArm {
|
||||
return strings.HasPrefix(distro.DTModel(), "TI AM335x BeagleBone Green")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// driver implements periph.Driver.
|
||||
type driver struct {
|
||||
}
|
||||
|
||||
func (d *driver) String() string {
|
||||
return "beaglebone-green"
|
||||
}
|
||||
|
||||
func (d *driver) Prerequisites() []string {
|
||||
return []string{"am335x", "sysfs-gpio"}
|
||||
}
|
||||
|
||||
func (d *driver) After() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) Init() (bool, error) {
|
||||
if !Present() {
|
||||
return false, errors.New("BeagleBone Green board not detected")
|
||||
}
|
||||
|
||||
I2C_SDA = sysfs.Pins[12]
|
||||
I2C_SCL = sysfs.Pins[13]
|
||||
hdr := [][]pin.Pin{{pin.GROUND}, {pin.V3_3}, {I2C_SDA}, {I2C_SCL}}
|
||||
if err := pinreg.Register("I2C", hdr); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
UART_TX = sysfs.Pins[3]
|
||||
UART_RX = sysfs.Pins[2]
|
||||
hdr = [][]pin.Pin{{pin.GROUND}, {pin.V3_3}, {UART_TX}, {UART_RX}}
|
||||
if err := pinreg.Register("UART", hdr); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
if isArm {
|
||||
driverreg.MustRegister(&drv)
|
||||
}
|
||||
}
|
||||
|
||||
var drv driver
|
7
vendor/periph.io/x/host/v3/beagle/green/green_arm.go
generated
vendored
Normal file
7
vendor/periph.io/x/host/v3/beagle/green/green_arm.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package green
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/beagle/green/green_other.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/beagle/green/green_other.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm
|
||||
|
||||
package green
|
||||
|
||||
const isArm = false
|
358
vendor/periph.io/x/host/v3/chip/chip.go
generated
vendored
Normal file
358
vendor/periph.io/x/host/v3/chip/chip.go
generated
vendored
Normal file
@ -0,0 +1,358 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package chip
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"periph.io/x/conn/v3/driver/driverreg"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/gpio/gpioreg"
|
||||
"periph.io/x/conn/v3/pin"
|
||||
"periph.io/x/conn/v3/pin/pinreg"
|
||||
"periph.io/x/host/v3/allwinner"
|
||||
"periph.io/x/host/v3/distro"
|
||||
"periph.io/x/host/v3/fs"
|
||||
)
|
||||
|
||||
// C.H.I.P. hardware pins.
|
||||
var (
|
||||
TEMP_SENSOR = &pin.BasicPin{N: "TEMP_SENSOR"}
|
||||
PWR_SWITCH = &pin.BasicPin{N: "PWR_SWITCH"}
|
||||
// XIO "gpio" pins attached to the pcf8574 I²C port extender.
|
||||
XIO0, XIO1, XIO2, XIO3, XIO4, XIO5, XIO6, XIO7 gpio.PinIO
|
||||
)
|
||||
|
||||
// The U13 header is opposite the power LED.
|
||||
//
|
||||
// The alternate pin functionality is described at pages 322-323 of
|
||||
// https://github.com/NextThingCo/CHIP-Hardware/raw/master/CHIP%5Bv1_0%5D/CHIPv1_0-BOM-Datasheets/Allwinner%20R8%20User%20Manual%20V1.1.pdf
|
||||
var (
|
||||
U13_1 = pin.GROUND //
|
||||
U13_2 = pin.DC_IN //
|
||||
U13_3 = pin.V5 // (filtered)
|
||||
U13_4 = pin.GROUND //
|
||||
U13_5 = pin.V3_3 //
|
||||
U13_6 = TEMP_SENSOR // Analog temp sensor input
|
||||
U13_7 = pin.V1_8 //
|
||||
U13_8 = pin.BAT_PLUS // External LiPo battery
|
||||
U13_9 = allwinner.PB16 // I2C1_SDA
|
||||
U13_10 = PWR_SWITCH // Power button
|
||||
U13_11 = allwinner.PB15 // I2C1_SCL
|
||||
U13_12 = pin.GROUND //
|
||||
U13_13 = allwinner.X1 // Touch screen X1
|
||||
U13_14 = allwinner.X2 // Touch screen X2
|
||||
U13_15 = allwinner.Y1 // Touch screen Y1
|
||||
U13_16 = allwinner.Y2 // Touch screen Y2
|
||||
U13_17 = allwinner.PD2 // LCD-D2; UART2_TX firmware probe for 1-wire to detect DIP at boot; http://docs.getchip.com/dip.html#dip-identification
|
||||
U13_18 = allwinner.PB2 // PWM0; EINT16
|
||||
U13_19 = allwinner.PD4 // LCD-D4; UART2_CTS
|
||||
U13_20 = allwinner.PD3 // LCD-D3; UART2_RX
|
||||
U13_21 = allwinner.PD6 // LCD-D6
|
||||
U13_22 = allwinner.PD5 // LCD-D5
|
||||
U13_23 = allwinner.PD10 // LCD-D10
|
||||
U13_24 = allwinner.PD7 // LCD-D7
|
||||
U13_25 = allwinner.PD12 // LCD-D12
|
||||
U13_26 = allwinner.PD11 // LCD-D11
|
||||
U13_27 = allwinner.PD14 // LCD-D14
|
||||
U13_28 = allwinner.PD13 // LCD-D13
|
||||
U13_29 = allwinner.PD18 // LCD-D18
|
||||
U13_30 = allwinner.PD15 // LCD-D15
|
||||
U13_31 = allwinner.PD20 // LCD-D20
|
||||
U13_32 = allwinner.PD19 // LCD-D19
|
||||
U13_33 = allwinner.PD22 // LCD-D22
|
||||
U13_34 = allwinner.PD21 // LCD-D21
|
||||
U13_35 = allwinner.PD24 // LCD-CLK
|
||||
U13_36 = allwinner.PD23 // LCD-D23
|
||||
U13_37 = allwinner.PD26 // LCD-VSYNC
|
||||
U13_38 = allwinner.PD27 // LCD-HSYNC
|
||||
U13_39 = pin.GROUND //
|
||||
U13_40 = allwinner.PD25 // LCD-DE: RGB666 data
|
||||
)
|
||||
|
||||
// The U14 header is right next to the power LED.
|
||||
var (
|
||||
U14_1 = pin.GROUND //
|
||||
U14_2 = pin.V5 // (filtered)
|
||||
U14_3 = allwinner.PG3 // UART1_TX; EINT3
|
||||
U14_4 = allwinner.HP_LEFT // Headphone left output
|
||||
U14_5 = allwinner.PG4 // UART1_RX; EINT4
|
||||
U14_6 = allwinner.HP_COM // Headphone amp out
|
||||
U14_7 = allwinner.FEL // Boot mode selection
|
||||
U14_8 = allwinner.HP_RIGHT // Headphone right output
|
||||
U14_9 = pin.V3_3 //
|
||||
U14_10 = allwinner.MIC_GND // Microphone ground
|
||||
U14_11 = allwinner.KEY_ADC // LRADC Low res analog to digital
|
||||
U14_12 = allwinner.MIC_IN // Microphone input
|
||||
U14_13 = XIO0 // gpio via I²C controller
|
||||
U14_14 = XIO1 // gpio via I²C controller
|
||||
U14_15 = XIO2 // gpio via I²C controller
|
||||
U14_16 = XIO3 // gpio via I²C controller
|
||||
U14_17 = XIO4 // gpio via I²C controller
|
||||
U14_18 = XIO5 // gpio via I²C controller
|
||||
U14_19 = XIO6 // gpio via I²C controller
|
||||
U14_20 = XIO7 // gpio via I²C controller
|
||||
U14_21 = pin.GROUND //
|
||||
U14_22 = pin.GROUND //
|
||||
U14_23 = allwinner.PG1 // GPS_CLK; AP-EINT1
|
||||
U14_24 = allwinner.PB3 // IR_TX; AP-EINT3 (EINT17)
|
||||
U14_25 = allwinner.PB18 // I2C2_SDA
|
||||
U14_26 = allwinner.PB17 // I2C2_SCL
|
||||
U14_27 = allwinner.PE0 // CSIPCK: CMOS serial interface; SPI2_CS0; EINT14
|
||||
U14_28 = allwinner.PE1 // CSICK: CMOS serial interface; SPI2_CLK; EINT15
|
||||
U14_29 = allwinner.PE2 // CSIHSYNC; SPI2_MOSI
|
||||
U14_30 = allwinner.PE3 // CSIVSYNC; SPI2_MISO
|
||||
U14_31 = allwinner.PE4 // CSID0
|
||||
U14_32 = allwinner.PE5 // CSID1
|
||||
U14_33 = allwinner.PE6 // CSID2
|
||||
U14_34 = allwinner.PE7 // CSID3
|
||||
U14_35 = allwinner.PE8 // CSID4
|
||||
U14_36 = allwinner.PE9 // CSID5
|
||||
U14_37 = allwinner.PE10 // CSID6; UART1_RX
|
||||
U14_38 = allwinner.PE11 // CSID7; UART1_TX
|
||||
U14_39 = pin.GROUND //
|
||||
U14_40 = pin.GROUND //
|
||||
)
|
||||
|
||||
// Present returns true if running on a NextThing Co's C.H.I.P. board.
|
||||
//
|
||||
// It looks for "C.H.I.P" in the device tree. The following information is
|
||||
// expected in the device dtree:
|
||||
// root@chip2:/proc/device-tree# od -c compatible
|
||||
// 0000000 n e x t t h i n g , c h i p \0 a
|
||||
// 0000020 l l w i n n e r , s u n 5 i - r
|
||||
// 0000040 8 \0
|
||||
// root@chip2:/proc/device-tree# od -c model
|
||||
// 0000000 N e x t T h i n g C . H . I .
|
||||
// 0000020 P . \0
|
||||
func Present() bool {
|
||||
return strings.Contains(distro.DTModel(), "C.H.I.P")
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// aliases is a list of aliases for the various gpio pins, this allows users to
|
||||
// refer to pins using the documented and labeled names instead of some GPIOnnn
|
||||
// name. The map key is the alias and the value is the real pin name.
|
||||
var aliases = map[string]string{
|
||||
"AP-EINT1": "PG1",
|
||||
"AP-EINT3": "PB3",
|
||||
"CSIPCK": "PE0",
|
||||
"CSIHSYNC": "PE2",
|
||||
"CSID0": "PE4",
|
||||
"CSID2": "PE6",
|
||||
"CSID4": "PE8",
|
||||
"CSID6": "PE10",
|
||||
"CSICK": "PE1",
|
||||
"CSIVSYNC": "PE3",
|
||||
"CSID1": "PE5",
|
||||
"CSID3": "PE7",
|
||||
"CSID5": "PE9",
|
||||
"CSID7": "PE11",
|
||||
"LCD-CLK": "PD24",
|
||||
"LCD-D10": "PD10",
|
||||
"LCD-D11": "PD11",
|
||||
"LCD-D12": "PD12",
|
||||
"LCD-D13": "PD13",
|
||||
"LCD-D14": "PD14",
|
||||
"LCD-D15": "PD15",
|
||||
"LCD-D18": "PD18",
|
||||
"LCD-D19": "PD19",
|
||||
"LCD-D2": "PD2",
|
||||
"LCD-D20": "PD20",
|
||||
"LCD-D21": "PD21",
|
||||
"LCD-D22": "PD22",
|
||||
"LCD-D23": "PD23",
|
||||
"LCD-D3": "PD3",
|
||||
"LCD-D4": "PD4",
|
||||
"LCD-D5": "PD5",
|
||||
"LCD-D6": "PD6",
|
||||
"LCD-D7": "PD7",
|
||||
"LCD-DE": "PD25",
|
||||
"LCD-HSYNC": "PD27",
|
||||
"LCD-VSYNC": "PD26",
|
||||
"TWI1-SCK": "PB15",
|
||||
"TWI1-SDA": "PB16",
|
||||
"TWI2-SCK": "PB17",
|
||||
"TWI2-SDA": "PB18",
|
||||
"UART1-RX": "PG4",
|
||||
"UART1-TX": "PG3",
|
||||
}
|
||||
|
||||
func init() {
|
||||
// These are initialized later by the driver.
|
||||
XIO0 = gpio.INVALID
|
||||
XIO1 = gpio.INVALID
|
||||
XIO2 = gpio.INVALID
|
||||
XIO3 = gpio.INVALID
|
||||
XIO4 = gpio.INVALID
|
||||
XIO5 = gpio.INVALID
|
||||
XIO6 = gpio.INVALID
|
||||
XIO7 = gpio.INVALID
|
||||
// These must be reinitialized.
|
||||
U14_13 = XIO0
|
||||
U14_14 = XIO1
|
||||
U14_15 = XIO2
|
||||
U14_16 = XIO3
|
||||
U14_17 = XIO4
|
||||
U14_18 = XIO5
|
||||
U14_19 = XIO6
|
||||
U14_20 = XIO7
|
||||
}
|
||||
|
||||
// findXIOBase calculates the base of the XIO-P? gpio pins as explained in
|
||||
// http://docs.getchip.com/chip.html#kernel-4-3-vs-4-4-gpio-how-to-tell-the-difference
|
||||
//
|
||||
// The XIO-P? sysfs mapped pin number changed in kernel 4.3, 4.4.11 and again
|
||||
// in 4.4.13 so it is better to query sysfs.
|
||||
func findXIOBase() int {
|
||||
chips, err := filepath.Glob("/sys/class/gpio/gpiochip*/label")
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
for _, item := range chips {
|
||||
f, err := fs.Open(item, os.O_RDONLY)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
b, err := ioutil.ReadAll(f)
|
||||
if err1 := f.Close(); err == nil {
|
||||
err = err1
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if string(b) == "pcf8574a\n" {
|
||||
id, err := strconv.Atoi(filepath.Base(filepath.Dir(item))[8:])
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return id
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// driver implements drivers.Driver.
|
||||
type driver struct {
|
||||
}
|
||||
|
||||
func (d *driver) String() string {
|
||||
return "chip"
|
||||
}
|
||||
|
||||
func (d *driver) Prerequisites() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) After() []string {
|
||||
// has allwinner cpu, needs sysfs for XIO0-XIO7 "gpio" pins
|
||||
return []string{"allwinner-gpio", "sysfs-gpio"}
|
||||
}
|
||||
|
||||
func (d *driver) Init() (bool, error) {
|
||||
if !Present() {
|
||||
return false, errors.New("NextThing Co. CHIP board not detected")
|
||||
}
|
||||
|
||||
base := findXIOBase()
|
||||
if base == -1 {
|
||||
return true, errors.New("couldn't find XIO pins base number")
|
||||
}
|
||||
for i := 0; i < 8; i++ {
|
||||
aliases[fmt.Sprintf("XIO-P%d", i)] = fmt.Sprintf("GPIO%d", base+i)
|
||||
}
|
||||
|
||||
// At this point the sysfs driver has initialized and discovered its pins,
|
||||
// we can now hook-up the appropriate CHIP pins to sysfs gpio pins.
|
||||
for alias, real := range aliases {
|
||||
if err := gpioreg.RegisterAlias(alias, real); err != nil {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
// These must be explicitly initialized.
|
||||
XIO0 = gpioreg.ByName("XIO-P0")
|
||||
XIO1 = gpioreg.ByName("XIO-P1")
|
||||
XIO2 = gpioreg.ByName("XIO-P2")
|
||||
XIO3 = gpioreg.ByName("XIO-P3")
|
||||
XIO4 = gpioreg.ByName("XIO-P4")
|
||||
XIO5 = gpioreg.ByName("XIO-P5")
|
||||
XIO6 = gpioreg.ByName("XIO-P6")
|
||||
XIO7 = gpioreg.ByName("XIO-P7")
|
||||
U14_13 = XIO0
|
||||
U14_14 = XIO1
|
||||
U14_15 = XIO2
|
||||
U14_16 = XIO3
|
||||
U14_17 = XIO4
|
||||
U14_18 = XIO5
|
||||
U14_19 = XIO6
|
||||
U14_20 = XIO7
|
||||
|
||||
// U13 is one of the 20x2 connectors.
|
||||
U13 := [][]pin.Pin{
|
||||
{U13_1, U13_2},
|
||||
{U13_3, U13_4},
|
||||
{U13_5, U13_6},
|
||||
{U13_7, U13_8},
|
||||
{gpioreg.ByName("TWI1-SDA"), U13_10},
|
||||
{gpioreg.ByName("TWI1-SCK"), U13_12},
|
||||
{U13_13, U13_14},
|
||||
{U13_15, U13_16},
|
||||
{gpioreg.ByName("LCD-D2"), gpioreg.ByName("PWM0")},
|
||||
{gpioreg.ByName("LCD-D4"), gpioreg.ByName("LCD-D3")},
|
||||
{gpioreg.ByName("LCD-D6"), gpioreg.ByName("LCD-D5")},
|
||||
{gpioreg.ByName("LCD-D10"), gpioreg.ByName("LCD-D7")},
|
||||
{gpioreg.ByName("LCD-D12"), gpioreg.ByName("LCD-D11")},
|
||||
{gpioreg.ByName("LCD-D14"), gpioreg.ByName("LCD-D13")},
|
||||
{gpioreg.ByName("LCD-D18"), gpioreg.ByName("LCD-D15")},
|
||||
{gpioreg.ByName("LCD-D20"), gpioreg.ByName("LCD-D19")},
|
||||
{gpioreg.ByName("LCD-D22"), gpioreg.ByName("LCD-D21")},
|
||||
{gpioreg.ByName("LCD-CLK"), gpioreg.ByName("LCD-D23")},
|
||||
{gpioreg.ByName("LCD-VSYNC"), gpioreg.ByName("LCD-HSYNC")},
|
||||
{U13_39, gpioreg.ByName("LCD-DE")},
|
||||
}
|
||||
if err := pinreg.Register("U13", U13); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
// U14 is one of the 20x2 connectors.
|
||||
U14 := [][]pin.Pin{
|
||||
{U14_1, U14_2},
|
||||
{gpioreg.ByName("UART1-TX"), U14_4},
|
||||
{gpioreg.ByName("UART1-RX"), U14_6},
|
||||
{U14_7, U14_8},
|
||||
{U14_9, U14_10},
|
||||
{U14_11, U14_12}, // TODO(maruel): switch to LRADC once analog support is added
|
||||
{U14_13, U14_14},
|
||||
{U14_15, U14_16},
|
||||
{U14_17, U14_18},
|
||||
{U14_19, U14_20},
|
||||
{U14_21, U14_22},
|
||||
{gpioreg.ByName("AP-EINT1"), gpioreg.ByName("AP-EINT3")},
|
||||
{gpioreg.ByName("TWI2-SDA"), gpioreg.ByName("TWI2-SCK")},
|
||||
{gpioreg.ByName("CSIPCK"), gpioreg.ByName("CSICK")},
|
||||
{gpioreg.ByName("CSIHSYNC"), gpioreg.ByName("CSIVSYNC")},
|
||||
{gpioreg.ByName("CSID0"), gpioreg.ByName("CSID1")},
|
||||
{gpioreg.ByName("CSID2"), gpioreg.ByName("CSID3")},
|
||||
{gpioreg.ByName("CSID4"), gpioreg.ByName("CSID5")},
|
||||
{gpioreg.ByName("CSID6"), gpioreg.ByName("CSID7")},
|
||||
{U14_39, U14_40},
|
||||
}
|
||||
return true, pinreg.Register("U14", U14)
|
||||
}
|
||||
|
||||
func init() {
|
||||
if isArm {
|
||||
driverreg.MustRegister(&drv)
|
||||
}
|
||||
}
|
||||
|
||||
var drv driver
|
7
vendor/periph.io/x/host/v3/chip/chip_arm.go
generated
vendored
Normal file
7
vendor/periph.io/x/host/v3/chip/chip_arm.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package chip
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/chip/chip_other.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/chip/chip_other.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm
|
||||
|
||||
package chip
|
||||
|
||||
const isArm = false
|
30
vendor/periph.io/x/host/v3/chip/doc.go
generated
vendored
Normal file
30
vendor/periph.io/x/host/v3/chip/doc.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package chip contains header definitions for NextThing Co's C.H.I.P. board.
|
||||
//
|
||||
// CHIP uses the Allwinner R8 processor and thus the allwinner host package is
|
||||
// automatically imported.
|
||||
//
|
||||
// This package exports the U13 header, which is opposite the power LED, and
|
||||
// U14, which is right next to the power LED. Most of the pins are usable as
|
||||
// GPIO and are directly to the processor. These can use memory-mapped GPIO,
|
||||
// which is very fast. The XIO-P0 through XIO-P7 pins are attached to a pcf8574
|
||||
// I²C expander which has the result that all accesses to these pins have to go
|
||||
// through the kernel and the I²C bus protocol, i.e., they're slow.
|
||||
//
|
||||
// GPIO edge detection (using interrupts) is only supported on a few of the
|
||||
// processor's pins: AP-EINT1, AP-EINT3, CSIPCK, and CSICK. Edge detection is
|
||||
// also supported on the XIO pins, but this feature is rather limited due to
|
||||
// the device and the driver (for example, the driver interrupts on all edges).
|
||||
//
|
||||
// References
|
||||
//
|
||||
// http://www.chip-community.org/index.php/Hardware_Information
|
||||
//
|
||||
// http://docs.getchip.com/chip.html#chip-hardware
|
||||
//
|
||||
// A graphical view of the board headers is available at:
|
||||
// http://docs.getchip.com/chip.html#pin-headers
|
||||
package chip
|
20
vendor/periph.io/x/host/v3/codecov.yml
generated
vendored
Normal file
20
vendor/periph.io/x/host/v3/codecov.yml
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
# Copyright 2020 The Periph Authors. All rights reserved.
|
||||
# Use of this source code is governed under the Apache License, Version 2.0
|
||||
# that can be found in the LICENSE file.
|
||||
|
||||
# https://docs.codecov.io/docs/codecovyml-reference
|
||||
# and
|
||||
# https://docs.codecov.io/docs/coverage-configuration
|
||||
coverage:
|
||||
precision: 1
|
||||
range: "40...80"
|
||||
round: nearest
|
||||
status:
|
||||
patch:
|
||||
default:
|
||||
target: 60%
|
||||
threshold: 10%
|
||||
project:
|
||||
default:
|
||||
target: 60%
|
||||
threshold: 10%
|
83
vendor/periph.io/x/host/v3/cpu/cpu.go
generated
vendored
Normal file
83
vendor/periph.io/x/host/v3/cpu/cpu.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"periph.io/x/host/v3/fs"
|
||||
)
|
||||
|
||||
// MaxSpeed returns the processor maximum speed in Hz.
|
||||
//
|
||||
// Returns 0 if it couldn't be calculated.
|
||||
func MaxSpeed() int64 {
|
||||
if isLinux {
|
||||
return getMaxSpeedLinux()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Nanospin spins for a short amount of time doing a busy loop.
|
||||
//
|
||||
// This function should be called with durations of 10µs or less.
|
||||
func Nanospin(d time.Duration) {
|
||||
// TODO(maruel): Use runtime.LockOSThread()?
|
||||
if isLinux {
|
||||
nanospinLinux(d)
|
||||
} else {
|
||||
nanospinTime(d)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
maxSpeed int64 = -1
|
||||
openFile = openFileOrig
|
||||
)
|
||||
|
||||
func openFileOrig(path string, flag int) (io.ReadCloser, error) {
|
||||
f, err := fs.Open(path, flag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func getMaxSpeedLinux() int64 {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if maxSpeed == -1 {
|
||||
maxSpeed = 0
|
||||
if f, err := openFile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", os.O_RDONLY); err == nil {
|
||||
defer f.Close()
|
||||
if b, err := ioutil.ReadAll(f); err == nil {
|
||||
s := strings.TrimSpace(string(b))
|
||||
if i, err := strconv.ParseInt(s, 10, 64); err == nil {
|
||||
// Weirdly, the speed is listed as khz. :(
|
||||
maxSpeed = i * 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxSpeed
|
||||
}
|
||||
|
||||
func nanospinTime(d time.Duration) {
|
||||
// TODO(maruel): That's not optimal; it's actually pretty bad.
|
||||
// time.Sleep() sleeps for really too long, calling it repeatedly with
|
||||
// minimal value will give the caller a wake rate of 5KHz or so, depending on
|
||||
// the host. This makes it useless for bitbanging protocol implementations.
|
||||
for start := time.Now(); time.Since(start) < d; {
|
||||
}
|
||||
}
|
22
vendor/periph.io/x/host/v3/cpu/cpu_linux.go
generated
vendored
Normal file
22
vendor/periph.io/x/host/v3/cpu/cpu_linux.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const isLinux = true
|
||||
|
||||
func nanospinLinux(d time.Duration) {
|
||||
// runtime.nanotime() is not exported so it cannot be used to busy loop for
|
||||
// very short sleep (10µs or less).
|
||||
time := syscall.NsecToTimespec(d.Nanoseconds())
|
||||
leftover := syscall.Timespec{}
|
||||
for syscall.Nanosleep(&time, &leftover) != nil {
|
||||
time = leftover
|
||||
}
|
||||
}
|
14
vendor/periph.io/x/host/v3/cpu/cpu_other.go
generated
vendored
Normal file
14
vendor/periph.io/x/host/v3/cpu/cpu_other.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !linux
|
||||
|
||||
package cpu
|
||||
|
||||
import "time"
|
||||
|
||||
const isLinux = false
|
||||
|
||||
func nanospinLinux(d time.Duration) {
|
||||
}
|
6
vendor/periph.io/x/host/v3/cpu/doc.go
generated
vendored
Normal file
6
vendor/periph.io/x/host/v3/cpu/doc.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package cpu implements functions relating to the host CPU itself.
|
||||
package cpu
|
84
vendor/periph.io/x/host/v3/distro/devtree.go
generated
vendored
Normal file
84
vendor/periph.io/x/host/v3/distro/devtree.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package distro
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// DTModel returns platform model info from the Linux device tree (/proc/device-tree/model), and
|
||||
// returns "unknown" on non-linux systems or if the file is missing.
|
||||
func DTModel() string {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if dtModel == "" {
|
||||
dtModel = "<unknown>"
|
||||
if isLinux {
|
||||
dtModel = makeDTModelLinux()
|
||||
}
|
||||
}
|
||||
return dtModel
|
||||
}
|
||||
|
||||
// DTCompatible returns platform compatibility info from the Linux device tree
|
||||
// (/proc/device-tree/compatible), and returns []{"unknown"} on non-linux systems or if the file is
|
||||
// missing.
|
||||
func DTCompatible() []string {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if dtCompatible == nil {
|
||||
dtCompatible = []string{}
|
||||
if isLinux {
|
||||
dtCompatible = makeDTCompatible()
|
||||
}
|
||||
}
|
||||
return dtCompatible
|
||||
}
|
||||
|
||||
// DTRevision returns the device revision (e.g. a02082 for the Raspberry Pi 3)
|
||||
// from the Linux device tree, or 0 if the file is missing or malformed.
|
||||
func DTRevision() uint32 {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if dtRevisionRead {
|
||||
return dtRevision
|
||||
}
|
||||
dtRevisionRead = true
|
||||
if b, _ := ioutil.ReadFile("/proc/device-tree/system/linux,revision"); len(b) >= 4 {
|
||||
dtRevision = binary.BigEndian.Uint32(b[:4])
|
||||
}
|
||||
return dtRevision
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var (
|
||||
dtModel string // cached /proc/device-tree/model
|
||||
dtCompatible []string // cached /proc/device-tree/compatible
|
||||
dtRevision uint32 // cached /proc/device-tree/system/linux,revision
|
||||
dtRevisionRead bool
|
||||
)
|
||||
|
||||
func makeDTModelLinux() string {
|
||||
// Read model from device tree.
|
||||
if bytes, err := readFile("/proc/device-tree/model"); err == nil {
|
||||
if model := splitNull(bytes); len(model) > 0 {
|
||||
return model[0]
|
||||
}
|
||||
}
|
||||
return "<unknown>"
|
||||
}
|
||||
|
||||
func makeDTCompatible() []string {
|
||||
// Read compatible from device tree.
|
||||
if bytes, err := readFile("/proc/device-tree/compatible"); err == nil {
|
||||
return splitNull(bytes)
|
||||
}
|
||||
return []string{}
|
||||
}
|
189
vendor/periph.io/x/host/v3/distro/distro.go
generated
vendored
Normal file
189
vendor/periph.io/x/host/v3/distro/distro.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package distro implements common functionality to auto-detect features on
|
||||
// the host; generally about linux distributions.
|
||||
//
|
||||
// Most of the functions exported as in the form IsFoo() where Foo is a linux
|
||||
// distribution.
|
||||
package distro
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// IsArmbian returns true if running on a Armbian distribution.
|
||||
//
|
||||
// http://www.armbian.com/
|
||||
func IsArmbian() bool {
|
||||
if isArm && isLinux {
|
||||
// Armbian presents itself as debian in /etc/os-release so OSRelease()
|
||||
// cannot be used..
|
||||
_, err := os.Stat("/etc/armbian.txt")
|
||||
return err == nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDebian returns true if running on an Debian derived distribution.
|
||||
//
|
||||
// This function returns true on both Armbian, Raspbian and Ubuntu.
|
||||
//
|
||||
// https://debian.org/
|
||||
func IsDebian() bool {
|
||||
if isLinux {
|
||||
// http://0pointer.de/public/systemd-man/os-release.html#ID_LIKE=
|
||||
if OSRelease()["ID"] == "debian" {
|
||||
return true
|
||||
}
|
||||
for _, part := range strings.Split(OSRelease()["ID_LIKE"], " ") {
|
||||
if part == "debian" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRaspbian returns true if running on a Raspbian distribution.
|
||||
//
|
||||
// https://raspbian.org/
|
||||
func IsRaspbian() bool {
|
||||
if isArm && isLinux {
|
||||
return OSRelease()["ID"] == "raspbian"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsUbuntu returns true if running on an Ubuntu derived distribution.
|
||||
//
|
||||
// https://ubuntu.com/
|
||||
func IsUbuntu() bool {
|
||||
if isLinux {
|
||||
return OSRelease()["ID"] == "ubuntu"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// OSRelease returns parsed data from /etc/os-release.
|
||||
//
|
||||
// For more information, see
|
||||
// http://0pointer.de/public/systemd-man/os-release.html
|
||||
func OSRelease() map[string]string {
|
||||
if isLinux {
|
||||
return makeOSReleaseLinux()
|
||||
}
|
||||
return osRelease
|
||||
}
|
||||
|
||||
// CPU
|
||||
|
||||
// CPUInfo returns parsed data from /proc/cpuinfo.
|
||||
func CPUInfo() map[string]string {
|
||||
if isLinux {
|
||||
return makeCPUInfoLinux()
|
||||
}
|
||||
return cpuInfo
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
cpuInfo map[string]string
|
||||
osRelease map[string]string
|
||||
readFile = ioutil.ReadFile
|
||||
)
|
||||
|
||||
func splitSemiColon(content string) map[string]string {
|
||||
// Strictly speaking this format isn't ok, there can be multiple group.
|
||||
out := map[string]string{}
|
||||
for _, line := range strings.Split(content, "\n") {
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
// This format may have space around the ':'.
|
||||
key := strings.TrimRightFunc(parts[0], unicode.IsSpace)
|
||||
if len(key) == 0 || key[0] == '#' {
|
||||
continue
|
||||
}
|
||||
// Ignore duplicate keys.
|
||||
// TODO(maruel): Keep them all.
|
||||
if _, ok := out[key]; !ok {
|
||||
// Trim on both side, trailing space was observed on "Features" value.
|
||||
out[key] = strings.TrimFunc(parts[1], unicode.IsSpace)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func splitStrict(content string) map[string]string {
|
||||
out := map[string]string{}
|
||||
for _, line := range strings.Split(content, "\n") {
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
key := parts[0]
|
||||
if len(key) == 0 || key[0] == '#' {
|
||||
continue
|
||||
}
|
||||
// Overwrite previous key.
|
||||
value := parts[1]
|
||||
if len(value) > 2 && value[0] == '"' && value[len(value)-1] == '"' {
|
||||
// Not exactly 100% right but #closeenough. See for more details
|
||||
// https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||
var err error
|
||||
value, err = strconv.Unquote(value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
out[key] = value
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// splitNull returns the null-terminated strings in the data
|
||||
func splitNull(data []byte) []string {
|
||||
ss := strings.Split(string(data), "\x00")
|
||||
// The last string is typically null-terminated, so remove empty string
|
||||
// from end of array.
|
||||
if len(ss) > 0 && len(ss[len(ss)-1]) == 0 {
|
||||
ss = ss[:len(ss)-1]
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func makeCPUInfoLinux() map[string]string {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if cpuInfo == nil {
|
||||
cpuInfo = map[string]string{}
|
||||
if bytes, err := readFile("/proc/cpuinfo"); err == nil {
|
||||
cpuInfo = splitSemiColon(string(bytes))
|
||||
}
|
||||
}
|
||||
return cpuInfo
|
||||
}
|
||||
|
||||
func makeOSReleaseLinux() map[string]string {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if osRelease == nil {
|
||||
// This file may not exist on older distros. Send a PR if you want to have
|
||||
// a specific fallback.
|
||||
osRelease = map[string]string{}
|
||||
if bytes, err := readFile("/etc/os-release"); err == nil {
|
||||
osRelease = splitStrict(string(bytes))
|
||||
}
|
||||
}
|
||||
return osRelease
|
||||
}
|
7
vendor/periph.io/x/host/v3/distro/distro_arm.go
generated
vendored
Normal file
7
vendor/periph.io/x/host/v3/distro/distro_arm.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package distro
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/distro/distro_arm64.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/distro/distro_arm64.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build arm64
|
||||
|
||||
package distro
|
||||
|
||||
const isArm = true
|
7
vendor/periph.io/x/host/v3/distro/distro_linux.go
generated
vendored
Normal file
7
vendor/periph.io/x/host/v3/distro/distro_linux.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package distro
|
||||
|
||||
const isLinux = true
|
9
vendor/periph.io/x/host/v3/distro/distro_nonarm.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/distro/distro_nonarm.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm,!arm64
|
||||
|
||||
package distro
|
||||
|
||||
const isArm = false
|
9
vendor/periph.io/x/host/v3/distro/distro_nonlinux.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/distro/distro_nonlinux.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !linux
|
||||
|
||||
package distro
|
||||
|
||||
const isLinux = false
|
10
vendor/periph.io/x/host/v3/doc.go
generated
vendored
Normal file
10
vendor/periph.io/x/host/v3/doc.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package host defines the host itself.
|
||||
//
|
||||
// The host is the machine where this code is running.
|
||||
//
|
||||
// Subpackages contain the drivers that are loaded automatically.
|
||||
package host
|
100
vendor/periph.io/x/host/v3/fs/fs.go
generated
vendored
Normal file
100
vendor/periph.io/x/host/v3/fs/fs.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package fs provides access to the file system on the host.
|
||||
//
|
||||
// It exposes ioctl syscall and epoll in an OS agnostic way and permits
|
||||
// completely disabling file access to lock down unit tests.
|
||||
package fs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Ioctler is a file handle that supports ioctl calls.
|
||||
type Ioctler interface {
|
||||
// Ioctl sends a linux ioctl on the file handle.
|
||||
//
|
||||
// op is effectively an uint32. op is expected to be encoded in the format on
|
||||
// x64. ARM happens to share the same format.
|
||||
Ioctl(op uint, data uintptr) error
|
||||
}
|
||||
|
||||
// Open opens a file.
|
||||
//
|
||||
// Returns an error if Inhibit() was called.
|
||||
func Open(path string, flag int) (*File, error) {
|
||||
mu.Lock()
|
||||
if inhibited {
|
||||
mu.Unlock()
|
||||
return nil, errors.New("file I/O is inhibited")
|
||||
}
|
||||
used = true
|
||||
mu.Unlock()
|
||||
|
||||
f, err := os.OpenFile(path, flag, 0600)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &File{f}, nil
|
||||
}
|
||||
|
||||
// Inhibit inhibits any future file I/O. It panics if any file was opened up to
|
||||
// now.
|
||||
//
|
||||
// It should only be called in unit tests.
|
||||
func Inhibit() {
|
||||
mu.Lock()
|
||||
inhibited = true
|
||||
if used {
|
||||
panic("calling Inhibit() while files were already opened")
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
// File is a superset of os.File.
|
||||
type File struct {
|
||||
*os.File
|
||||
}
|
||||
|
||||
// Ioctl sends an ioctl to the file handle.
|
||||
func (f *File) Ioctl(op uint, data uintptr) error {
|
||||
return ioctl(f.Fd(), op, data)
|
||||
}
|
||||
|
||||
// Event is a file system event.
|
||||
type Event struct {
|
||||
event
|
||||
}
|
||||
|
||||
// MakeEvent initializes an epoll *edge* triggered event on linux.
|
||||
//
|
||||
// An edge triggered event is basically an "auto-reset" event, where waiting on
|
||||
// the edge resets it. A level triggered event requires manual resetting; this
|
||||
// could be done via a Read() call but there's no need to require the user to
|
||||
// call Read(). This is particularly useless in the case of gpio.RisingEdge and
|
||||
// gpio.FallingEdge.
|
||||
//
|
||||
// As per the official doc, edge triggers is still remembered even when no
|
||||
// epoll_wait() call is running, so no edge is missed. Two edges will be
|
||||
// coallesced into one if the user mode process can't keep up. There's no
|
||||
// accumulation of edges.
|
||||
func (e *Event) MakeEvent(fd uintptr) error {
|
||||
return e.event.makeEvent(fd)
|
||||
}
|
||||
|
||||
// Wait waits for an event or the specified amount of time.
|
||||
func (e *Event) Wait(timeoutms int) (int, error) {
|
||||
return e.event.wait(timeoutms)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
inhibited bool
|
||||
used bool
|
||||
)
|
124
vendor/periph.io/x/host/v3/fs/fs_linux.go
generated
vendored
Normal file
124
vendor/periph.io/x/host/v3/fs/fs_linux.go
generated
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const isLinux = true
|
||||
|
||||
// syscall.EpollCtl() commands.
|
||||
//
|
||||
// These are defined here so we don't need to import golang.org/x/sys/unix.
|
||||
//
|
||||
// http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
|
||||
const (
|
||||
epollCTLAdd = 1 // EPOLL_CTL_ADD
|
||||
epollCTLDel = 2 // EPOLL_CTL_DEL
|
||||
epollCTLMod = 3 // EPOLL_CTL_MOD
|
||||
)
|
||||
|
||||
// Bitmask for field syscall.EpollEvent.Events.
|
||||
//
|
||||
// These are defined here so we don't need to import golang.org/x/sys/unix.
|
||||
//
|
||||
// http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
|
||||
type epollEvent uint32
|
||||
|
||||
const (
|
||||
epollIN epollEvent = 0x1 // EPOLLIN: available for read
|
||||
epollOUT epollEvent = 0x4 // EPOLLOUT: available for write
|
||||
epollPRI epollEvent = 0x2 // EPOLLPRI: exceptional urgent condition
|
||||
epollERR epollEvent = 0x8 // EPOLLERR: error
|
||||
epollHUP epollEvent = 0x10 // EPOLLHUP: hangup
|
||||
epollET epollEvent = 0x80000000 // EPOLLET: Edge Triggered behavior
|
||||
epollONESHOT epollEvent = 0x40000000 // EPOLLONESHOT: One shot
|
||||
epollWAKEUP epollEvent = 0x20000000 // EPOLLWAKEUP: disable system sleep; kernel >=3.5
|
||||
epollEXCLUSIVE epollEvent = 0x10000000 // EPOLLEXCLUSIVE: only wake one; kernel >=4.5
|
||||
)
|
||||
|
||||
var bitmaskString = [...]struct {
|
||||
e epollEvent
|
||||
s string
|
||||
}{
|
||||
{epollIN, "IN"},
|
||||
{epollOUT, "OUT"},
|
||||
{epollPRI, "PRI"},
|
||||
{epollERR, "ERR"},
|
||||
{epollHUP, "HUP"},
|
||||
{epollET, "ET"},
|
||||
{epollONESHOT, "ONESHOT"},
|
||||
{epollWAKEUP, "WAKEUP"},
|
||||
{epollEXCLUSIVE, "EXCLUSIVE"},
|
||||
}
|
||||
|
||||
// String is useful for debugging.
|
||||
func (e epollEvent) String() string {
|
||||
var out []string
|
||||
for _, b := range bitmaskString {
|
||||
if e&b.e != 0 {
|
||||
out = append(out, b.s)
|
||||
e &^= b.e
|
||||
}
|
||||
}
|
||||
if e != 0 {
|
||||
out = append(out, "0x"+strconv.FormatUint(uint64(e), 16))
|
||||
}
|
||||
if len(out) == 0 {
|
||||
out = []string{"0"}
|
||||
}
|
||||
return strings.Join(out, "|")
|
||||
}
|
||||
|
||||
func ioctl(f uintptr, op uint, arg uintptr) error {
|
||||
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f, uintptr(op), arg); errno != 0 {
|
||||
return syscall.Errno(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type event struct {
|
||||
event [1]syscall.EpollEvent
|
||||
epollFd int
|
||||
fd int
|
||||
}
|
||||
|
||||
// makeEvent creates an epoll *edge* triggered event.
|
||||
//
|
||||
// References:
|
||||
// behavior and flags: http://man7.org/linux/man-pages/man7/epoll.7.html
|
||||
// syscall.EpollCreate: http://man7.org/linux/man-pages/man2/epoll_create.2.html
|
||||
// syscall.EpollCtl: http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
|
||||
func (e *event) makeEvent(fd uintptr) error {
|
||||
epollFd, err := syscall.EpollCreate(1)
|
||||
switch {
|
||||
case err == nil:
|
||||
break
|
||||
case err.Error() == "function not implemented":
|
||||
// Some arch (arm64) do not implement EpollCreate().
|
||||
if epollFd, err = syscall.EpollCreate1(0); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return err
|
||||
}
|
||||
e.epollFd = epollFd
|
||||
e.fd = int(fd)
|
||||
// EPOLLWAKEUP could be used to force the system to not go do sleep while
|
||||
// waiting for an edge. This is generally a bad idea, as we'd instead have
|
||||
// the system to *wake up* when an edge is triggered. Achieving this is
|
||||
// outside the scope of this interface.
|
||||
e.event[0].Events = uint32(epollPRI | epollET)
|
||||
e.event[0].Fd = int32(e.fd)
|
||||
return syscall.EpollCtl(e.epollFd, epollCTLAdd, e.fd, &e.event[0])
|
||||
}
|
||||
|
||||
func (e *event) wait(timeoutms int) (int, error) {
|
||||
// http://man7.org/linux/man-pages/man2/epoll_wait.2.html
|
||||
return syscall.EpollWait(e.epollFd, e.event[:], timeoutms)
|
||||
}
|
25
vendor/periph.io/x/host/v3/fs/fs_other.go
generated
vendored
Normal file
25
vendor/periph.io/x/host/v3/fs/fs_other.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !linux
|
||||
|
||||
package fs
|
||||
|
||||
import "errors"
|
||||
|
||||
const isLinux = false
|
||||
|
||||
func ioctl(f uintptr, op uint, arg uintptr) error {
|
||||
return errors.New("fs: ioctl not supported on non-linux")
|
||||
}
|
||||
|
||||
type event struct{}
|
||||
|
||||
func (e *event) makeEvent(f uintptr) error {
|
||||
return errors.New("fs: unreachable code")
|
||||
}
|
||||
|
||||
func (e *event) wait(timeoutms int) (int, error) {
|
||||
return 0, errors.New("fs: unreachable code")
|
||||
}
|
51
vendor/periph.io/x/host/v3/fs/ioctl.go
generated
vendored
Normal file
51
vendor/periph.io/x/host/v3/fs/ioctl.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2019 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package fs
|
||||
|
||||
// These constants, variables and functions are ported from the Linux userland
|
||||
// API header ioctl.h (commonly packaged at /usr/include/linux/ioctl.h which
|
||||
// includes /usr/include/asm-generic/ioctl.h).
|
||||
|
||||
const (
|
||||
iocNrbits uint = 8
|
||||
iocTypebits uint = 8
|
||||
|
||||
iocNrshift uint = 0
|
||||
|
||||
iocTypeshift = iocNrshift + iocNrbits
|
||||
iocSizeshift = iocTypeshift + iocTypebits
|
||||
iocDirshift = iocSizeshift + iocSizebits
|
||||
)
|
||||
|
||||
func ioc(dir, typ, nr, size uint) uint {
|
||||
return (dir << iocDirshift) |
|
||||
(typ << iocTypeshift) |
|
||||
(nr << iocNrshift) |
|
||||
(size << iocSizeshift)
|
||||
}
|
||||
|
||||
// IO defines an ioctl with no parameters. It corresponds to _IO in the Linux
|
||||
// userland API.
|
||||
func IO(typ, nr uint) uint {
|
||||
return ioc(iocNone, typ, nr, 0)
|
||||
}
|
||||
|
||||
// IOR defines an ioctl with read (userland perspective) parameters. It
|
||||
// corresponds to _IOR in the Linux userland API.
|
||||
func IOR(typ, nr, size uint) uint {
|
||||
return ioc(iocRead, typ, nr, size)
|
||||
}
|
||||
|
||||
// IOW defines an ioctl with write (userland perspective) parameters. It
|
||||
// corresponds to _IOW in the Linux userland API.
|
||||
func IOW(typ, nr, size uint) uint {
|
||||
return ioc(iocWrite, typ, nr, size)
|
||||
}
|
||||
|
||||
// IOWR defines an ioctl with both read and write parameters. It corresponds to
|
||||
// _IOWR in the Linux userland API.
|
||||
func IOWR(typ, nr, size uint) uint {
|
||||
return ioc(iocRead|iocWrite, typ, nr, size)
|
||||
}
|
16
vendor/periph.io/x/host/v3/fs/ioctl_mips_like.go
generated
vendored
Normal file
16
vendor/periph.io/x/host/v3/fs/ioctl_mips_like.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build mips mipsle
|
||||
|
||||
package fs
|
||||
|
||||
const (
|
||||
iocNone uint = 1
|
||||
iocRead uint = 2
|
||||
iocWrite uint = 4
|
||||
|
||||
iocSizebits uint = 13
|
||||
iocDirbits uint = 3
|
||||
)
|
16
vendor/periph.io/x/host/v3/fs/ioctl_other.go
generated
vendored
Normal file
16
vendor/periph.io/x/host/v3/fs/ioctl_other.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !mips,!mipsle
|
||||
|
||||
package fs
|
||||
|
||||
const (
|
||||
iocNone uint = 0
|
||||
iocWrite uint = 1
|
||||
iocRead uint = 2
|
||||
|
||||
iocSizebits uint = 14
|
||||
iocDirbits uint = 2
|
||||
)
|
878
vendor/periph.io/x/host/v3/ftdi/dev.go
generated
vendored
Normal file
878
vendor/periph.io/x/host/v3/ftdi/dev.go
generated
vendored
Normal file
@ -0,0 +1,878 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package ftdi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"periph.io/x/conn/v3"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/i2c"
|
||||
"periph.io/x/conn/v3/physic"
|
||||
"periph.io/x/conn/v3/spi"
|
||||
)
|
||||
|
||||
// Info is the information gathered about the connected FTDI device.
|
||||
//
|
||||
// The data is gathered from the USB descriptor.
|
||||
type Info struct {
|
||||
// Opened is true if the device was successfully opened.
|
||||
Opened bool
|
||||
// Type is the FTDI device type.
|
||||
//
|
||||
// The value can be "FT232H", "FT232R", etc.
|
||||
//
|
||||
// An empty string means the type is unknown.
|
||||
Type string
|
||||
// VenID is the vendor ID from the USB descriptor information. It is expected
|
||||
// to be 0x0403 (FTDI).
|
||||
VenID uint16
|
||||
// DevID is the product ID from the USB descriptor information. It is
|
||||
// expected to be one of 0x6001, 0x6006, 0x6010, 0x6014.
|
||||
DevID uint16
|
||||
}
|
||||
|
||||
// Dev represents one FTDI device.
|
||||
//
|
||||
// There can be multiple FTDI devices connected to a host.
|
||||
//
|
||||
// The device may also export one or multiple of I²C, SPI buses. You need to
|
||||
// either cast into the right hardware, but more simply use the i2creg / spireg
|
||||
// bus/port registries.
|
||||
type Dev interface {
|
||||
// conn.Resource
|
||||
String() string
|
||||
Halt() error
|
||||
|
||||
// Info returns information about an opened device.
|
||||
Info(i *Info)
|
||||
|
||||
// Header returns the GPIO pins exposed on the chip.
|
||||
Header() []gpio.PinIO
|
||||
|
||||
// SetSpeed sets the base clock for all I/O transactions.
|
||||
//
|
||||
// The device defaults to its fastest speed.
|
||||
SetSpeed(f physic.Frequency) error
|
||||
|
||||
// EEPROM returns the EEPROM content.
|
||||
EEPROM(ee *EEPROM) error
|
||||
// WriteEEPROM updates the EEPROM. Must be used carefully.
|
||||
WriteEEPROM(ee *EEPROM) error
|
||||
// EraseEEPROM erases the EEPROM. Must be used carefully.
|
||||
EraseEEPROM() error
|
||||
// UserArea reads and return the EEPROM part that can be used to stored user
|
||||
// defined values.
|
||||
UserArea() ([]byte, error)
|
||||
// WriteUserArea updates the user area in the EEPROM.
|
||||
//
|
||||
// If the length of ua is less than the available space, is it zero extended.
|
||||
WriteUserArea(ua []byte) error
|
||||
}
|
||||
|
||||
// broken represents a device that couldn't be opened correctly.
|
||||
//
|
||||
// It returns an error message to help the user diagnose issues.
|
||||
type broken struct {
|
||||
index int
|
||||
err error
|
||||
name string
|
||||
}
|
||||
|
||||
func (b *broken) String() string {
|
||||
return b.name
|
||||
}
|
||||
|
||||
func (b *broken) Halt() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *broken) Info(i *Info) {
|
||||
i.Opened = false
|
||||
}
|
||||
|
||||
func (b *broken) Header() []gpio.PinIO {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *broken) SetSpeed(f physic.Frequency) error {
|
||||
return b.err
|
||||
}
|
||||
|
||||
func (b *broken) EEPROM(ee *EEPROM) error {
|
||||
return b.err
|
||||
}
|
||||
|
||||
func (b *broken) WriteEEPROM(ee *EEPROM) error {
|
||||
return b.err
|
||||
}
|
||||
|
||||
func (b *broken) EraseEEPROM() error {
|
||||
return b.err
|
||||
}
|
||||
|
||||
func (b *broken) UserArea() ([]byte, error) {
|
||||
return nil, b.err
|
||||
}
|
||||
|
||||
func (b *broken) WriteUserArea(ua []byte) error {
|
||||
return b.err
|
||||
}
|
||||
|
||||
// generic represents a generic FTDI device.
|
||||
//
|
||||
// It is used for the models that this package doesn't fully support yet.
|
||||
type generic struct {
|
||||
// Immutable after initialization.
|
||||
index int
|
||||
h *handle
|
||||
name string
|
||||
}
|
||||
|
||||
func (f *generic) String() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// Halt implements conn.Resource.
|
||||
//
|
||||
// This halts all operations going through this device.
|
||||
func (f *generic) Halt() error {
|
||||
return f.h.Reset()
|
||||
}
|
||||
|
||||
// Info returns information about an opened device.
|
||||
func (f *generic) Info(i *Info) {
|
||||
i.Opened = true
|
||||
i.Type = f.h.t.String()
|
||||
i.VenID = f.h.venID
|
||||
i.DevID = f.h.devID
|
||||
}
|
||||
|
||||
// Header returns the GPIO pins exposed on the chip.
|
||||
func (f *generic) Header() []gpio.PinIO {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *generic) SetSpeed(freq physic.Frequency) error {
|
||||
// TODO(maruel): Doc says the actual speed is 16x, confirm.
|
||||
return f.h.SetBaudRate(freq)
|
||||
}
|
||||
|
||||
func (f *generic) EEPROM(ee *EEPROM) error {
|
||||
return f.h.ReadEEPROM(ee)
|
||||
/*
|
||||
if f.ee.Raw == nil {
|
||||
if err := f.h.readEEPROM(&f.ee); err != nil {
|
||||
return nil
|
||||
}
|
||||
if f.ee.Raw == nil {
|
||||
// It's a fresh new device. Devices bought via Adafruit already have
|
||||
// their EEPROM programmed with Adafruit branding but devices sold by
|
||||
// CJMCU are not. Since d2xxGetDeviceInfo() above succeeded, we know the
|
||||
// device type via the USB descriptor, which is sufficient to load the
|
||||
// driver, which permits to program the EEPROM to "bootstrap" it.
|
||||
f.ee.Raw = []byte{}
|
||||
}
|
||||
}
|
||||
*ee = f.ee
|
||||
return nil
|
||||
*/
|
||||
}
|
||||
|
||||
func (f *generic) WriteEEPROM(ee *EEPROM) error {
|
||||
// TODO(maruel): Compare with the cached EEPROM, and only update the
|
||||
// different values if needed so reduce the EEPROM wear.
|
||||
// f.h.h.d2xxWriteEE()
|
||||
return f.h.WriteEEPROM(ee)
|
||||
}
|
||||
|
||||
func (f *generic) EraseEEPROM() error {
|
||||
return f.h.EraseEEPROM()
|
||||
}
|
||||
|
||||
func (f *generic) UserArea() ([]byte, error) {
|
||||
return f.h.ReadUA()
|
||||
}
|
||||
|
||||
func (f *generic) WriteUserArea(ua []byte) error {
|
||||
return f.h.WriteUA(ua)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
func newFT232H(g generic) (*FT232H, error) {
|
||||
f := &FT232H{
|
||||
generic: g,
|
||||
cbus: gpiosMPSSE{h: g.h, cbus: true},
|
||||
dbus: gpiosMPSSE{h: g.h},
|
||||
c8: invalidPin{num: 16, n: g.name + ".C8"}, // , dp: gpio.PullUp
|
||||
c9: invalidPin{num: 17, n: g.name + ".C9"}, // , dp: gpio.PullUp
|
||||
}
|
||||
f.cbus.init(f.name)
|
||||
f.dbus.init(f.name)
|
||||
|
||||
for i := range f.dbus.pins {
|
||||
f.hdr[i] = &f.dbus.pins[i]
|
||||
}
|
||||
for i := range f.cbus.pins {
|
||||
f.hdr[i+8] = &f.cbus.pins[i]
|
||||
}
|
||||
// TODO(maruel): C8 and C9 can be used when their mux in the EEPROM is set to
|
||||
// ft232hCBusIOMode.
|
||||
f.hdr[16] = &f.c8
|
||||
f.hdr[17] = &f.c9
|
||||
f.D0 = f.hdr[0]
|
||||
f.D1 = f.hdr[1]
|
||||
f.D2 = f.hdr[2]
|
||||
f.D3 = f.hdr[3]
|
||||
f.D4 = f.hdr[4]
|
||||
f.D5 = f.hdr[5]
|
||||
f.D6 = f.hdr[6]
|
||||
f.D7 = f.hdr[7]
|
||||
f.C0 = f.hdr[8]
|
||||
f.C1 = f.hdr[9]
|
||||
f.C2 = f.hdr[10]
|
||||
f.C3 = f.hdr[11]
|
||||
f.C4 = f.hdr[12]
|
||||
f.C5 = f.hdr[13]
|
||||
f.C6 = f.hdr[14]
|
||||
f.C7 = f.hdr[15]
|
||||
f.C8 = f.hdr[16]
|
||||
f.C9 = f.hdr[17]
|
||||
|
||||
// This function forces all pins as inputs.
|
||||
if err := f.h.InitMPSSE(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.s.c.f = f
|
||||
f.i.f = f
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// FT232H represents a FT232H device.
|
||||
//
|
||||
// It implements Dev.
|
||||
//
|
||||
// The FT232H has 1024 bytes output buffer and 1024 bytes input buffer. It
|
||||
// supports 512 bytes USB packets.
|
||||
//
|
||||
// The device can be used in a few different modes, two modes are supported:
|
||||
//
|
||||
// - D0~D3 as a serial protocol (MPSEE), supporting I²C and SPI (and eventually
|
||||
// UART), In this mode, D4~D7 and C0~C7 can be used as synchronized GPIO.
|
||||
//
|
||||
// - D0~D7 as a synchronous 8 bits bit-bang port. In this mode, only a few pins
|
||||
// on CBus are usable in slow mode.
|
||||
//
|
||||
// Each group of pins D0~D7 and C0~C7 can be changed at once in one pass via
|
||||
// DBus() or CBus().
|
||||
//
|
||||
// This enables usage as an 8 bit parallel port.
|
||||
//
|
||||
// Pins C8 and C9 can only be used in 'slow' mode via EEPROM and are currently
|
||||
// not implemented.
|
||||
//
|
||||
// Datasheet
|
||||
//
|
||||
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232H.pdf
|
||||
type FT232H struct {
|
||||
generic
|
||||
|
||||
D0 gpio.PinIO // Clock output
|
||||
D1 gpio.PinIO // Data out
|
||||
D2 gpio.PinIO // Data in
|
||||
D3 gpio.PinIO // Chip select
|
||||
D4 gpio.PinIO
|
||||
D5 gpio.PinIO
|
||||
D6 gpio.PinIO
|
||||
D7 gpio.PinIO
|
||||
C0 gpio.PinIO
|
||||
C1 gpio.PinIO
|
||||
C2 gpio.PinIO
|
||||
C3 gpio.PinIO
|
||||
C4 gpio.PinIO
|
||||
C5 gpio.PinIO
|
||||
C6 gpio.PinIO
|
||||
C7 gpio.PinIO
|
||||
C8 gpio.PinIO // Not implemented
|
||||
C9 gpio.PinIO // Not implemented
|
||||
|
||||
hdr [18]gpio.PinIO
|
||||
cbus gpiosMPSSE
|
||||
dbus gpiosMPSSE
|
||||
c8 invalidPin // gpio.PullUp
|
||||
c9 invalidPin // gpio.PullUp
|
||||
|
||||
mu sync.Mutex
|
||||
usingI2C bool
|
||||
usingSPI bool
|
||||
i i2cBus
|
||||
s spiMPSEEPort
|
||||
// TODO(maruel): Technically speaking, a SPI port could be hacked up too in
|
||||
// sync bit-bang but there's less point when MPSEE is available.
|
||||
}
|
||||
|
||||
// Header returns the GPIO pins exposed on the chip.
|
||||
func (f *FT232H) Header() []gpio.PinIO {
|
||||
out := make([]gpio.PinIO, len(f.hdr))
|
||||
copy(out, f.hdr[:])
|
||||
return out
|
||||
}
|
||||
|
||||
func (f *FT232H) SetSpeed(freq physic.Frequency) error {
|
||||
// TODO(maruel): When using MPSEE, use the MPSEE command. If using sync
|
||||
// bit-bang, use SetBaudRate().
|
||||
|
||||
// TODO(maruel): Doc says the actual speed is 16x, confirm.
|
||||
return f.h.SetBaudRate(freq)
|
||||
}
|
||||
|
||||
// CBus sets the values of C0 to C7 in the specified direction and value.
|
||||
//
|
||||
// 0 direction means input, 1 means output.
|
||||
func (f *FT232H) CBus(direction, value byte) error {
|
||||
return f.h.MPSSECBus(direction, value)
|
||||
}
|
||||
|
||||
// DBus sets the values of D0 to d7 in the specified direction and value.
|
||||
//
|
||||
// 0 direction means input, 1 means output.
|
||||
//
|
||||
// This function must be used to set Clock idle level.
|
||||
func (f *FT232H) DBus(direction, value byte) error {
|
||||
return f.h.MPSSEDBus(direction, value)
|
||||
}
|
||||
|
||||
// CBusRead reads the values of C0 to C7.
|
||||
func (f *FT232H) CBusRead() (byte, error) {
|
||||
return f.h.MPSSECBusRead()
|
||||
}
|
||||
|
||||
// DBusRead reads the values of D0 to D7.
|
||||
func (f *FT232H) DBusRead() (byte, error) {
|
||||
return f.h.MPSSEDBusRead()
|
||||
}
|
||||
|
||||
// I2C returns an I²C bus over the AD bus.
|
||||
//
|
||||
// pull can be either gpio.PullUp or gpio.Float. The recommended pull up
|
||||
// resistors are 10kΩ for 100kHz and 2kΩ for 400kHz when using Float. The
|
||||
// GPIO's pull up is 75kΩ, which may require using a lower speed for signal
|
||||
// reliability. Optimal pull up resistor calculation depends on the capacitance.
|
||||
//
|
||||
// It uses D0, D1 and D2.
|
||||
//
|
||||
// D0 is SCL. It must to be pulled up externally.
|
||||
//
|
||||
// D1 and D2 are used for SDA. D1 is the output using open drain, D2 is the
|
||||
// input. D1 and D2 must be wired together and must be pulled up externally.
|
||||
//
|
||||
// It is recommended to set the mode to ‘245 FIFO’ in the EEPROM of the FT232H.
|
||||
//
|
||||
// The FIFO mode is recommended because it allows the ADbus lines to start as
|
||||
// tristate. If the chip starts in the default UART mode, then the ADbus lines
|
||||
// will be in the default UART idle states until the application opens the port
|
||||
// and configures it as MPSSE. Care should also be taken that the RD# input on
|
||||
// ACBUS is not asserted in this initial state as this can cause the FIFO lines
|
||||
// to drive out.
|
||||
func (f *FT232H) I2C(pull gpio.Pull) (i2c.BusCloser, error) {
|
||||
if pull != gpio.PullUp && pull != gpio.Float {
|
||||
return nil, errors.New("d2xx: I²C pull can only be PullUp or Float")
|
||||
}
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
if f.usingI2C {
|
||||
return nil, errors.New("d2xx: already using I²C")
|
||||
}
|
||||
if f.usingSPI {
|
||||
return nil, errors.New("d2xx: already using SPI")
|
||||
}
|
||||
if err := f.i.setupI2C(pull == gpio.PullUp); err != nil {
|
||||
_ = f.i.stopI2C()
|
||||
return nil, err
|
||||
}
|
||||
return &f.i, nil
|
||||
}
|
||||
|
||||
// SPI returns a SPI port over the AD bus.
|
||||
//
|
||||
// It uses D0, D1, D2 and D3. D0 is the clock, D1 the output (MOSI), D2 is the
|
||||
// input (MISO) and D3 is CS line.
|
||||
func (f *FT232H) SPI() (spi.PortCloser, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
if f.usingI2C {
|
||||
return nil, errors.New("d2xx: already using I²C")
|
||||
}
|
||||
if f.usingSPI {
|
||||
return nil, errors.New("d2xx: already using SPI")
|
||||
}
|
||||
// Don't mark it as being used yet. It only become used once Connect() is
|
||||
// called.
|
||||
return &f.s, nil
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
func newFT232R(g generic) (*FT232R, error) {
|
||||
f := &FT232R{
|
||||
generic: g,
|
||||
dbus: [...]dbusPinSync{{num: 0}, {num: 1}, {num: 2}, {num: 3}, {num: 4}, {num: 5}, {num: 6}, {num: 7}},
|
||||
cbus: [...]cbusPin{{num: 8, p: gpio.PullUp}, {num: 9, p: gpio.PullUp}, {num: 10, p: gpio.PullUp}, {num: 11, p: gpio.Float}},
|
||||
}
|
||||
// Use the UART names, as this is how all FT232R boards are marked.
|
||||
dnames := [...]string{"TX", "RX", "RTS", "CTS", "DTR", "DSR", "DCD", "RI"}
|
||||
for i := range f.dbus {
|
||||
f.dbus[i].n = f.name + "." + dnames[i]
|
||||
f.dbus[i].bus = f
|
||||
f.hdr[i] = &f.dbus[i]
|
||||
}
|
||||
for i := range f.cbus {
|
||||
f.cbus[i].n = f.name + ".C" + strconv.Itoa(i)
|
||||
f.cbus[i].bus = f
|
||||
f.hdr[i+8] = &f.cbus[i]
|
||||
}
|
||||
f.D0 = f.hdr[0]
|
||||
f.D1 = f.hdr[1]
|
||||
f.D2 = f.hdr[2]
|
||||
f.D3 = f.hdr[3]
|
||||
f.D4 = f.hdr[4]
|
||||
f.D5 = f.hdr[5]
|
||||
f.D6 = f.hdr[6]
|
||||
f.D7 = f.hdr[7]
|
||||
f.TX = f.hdr[0]
|
||||
f.RX = f.hdr[1]
|
||||
f.RTS = f.hdr[2]
|
||||
f.CTS = f.hdr[3]
|
||||
f.DTR = f.hdr[4]
|
||||
f.DSR = f.hdr[5]
|
||||
f.DCD = f.hdr[6]
|
||||
f.RI = f.hdr[7]
|
||||
f.C0 = f.hdr[8]
|
||||
f.C1 = f.hdr[9]
|
||||
f.C2 = f.hdr[10]
|
||||
f.C3 = f.hdr[11]
|
||||
|
||||
// Default to 3MHz.
|
||||
if err := f.h.SetBaudRate(3 * physic.MegaHertz); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set all CBus pins as input.
|
||||
if err := f.h.SetBitMode(0, bitModeCbusBitbang); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// And read their value.
|
||||
// TODO(maruel): Sadly this is impossible to know which pin is input or
|
||||
// output, but we could try to guess, as the call above may generate noise on
|
||||
// the line which could interfere with the device connected.
|
||||
var err error
|
||||
if f.cbusnibble, err = f.h.GetBitMode(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Set all DBus as asynchronous bitbang, everything as input.
|
||||
if err := f.h.SetBitMode(0, bitModeAsyncBitbang); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// And read their value.
|
||||
var b [1]byte
|
||||
if _, err := f.h.ReadAll(context.Background(), b[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.dvalue = b[0]
|
||||
f.s.c.f = f
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// FT232R represents a FT232RL/FT232RQ device.
|
||||
//
|
||||
// It implements Dev.
|
||||
//
|
||||
// Not all pins may be physically connected on the header!
|
||||
//
|
||||
// Adafruit's version only has the following pins connected: RX, TX, RTS and
|
||||
// CTS.
|
||||
//
|
||||
// SparkFun's version exports all pins *except* (inexplicably) the CBus ones.
|
||||
//
|
||||
// The FT232R has 128 bytes output buffer and 256 bytes input buffer.
|
||||
//
|
||||
// Pin C4 can only be used in 'slow' mode via EEPROM and is currently not
|
||||
// implemented.
|
||||
//
|
||||
// Datasheet
|
||||
//
|
||||
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232R.pdf
|
||||
type FT232R struct {
|
||||
generic
|
||||
|
||||
// Pin and their alias to the Dn pins for user convenience. Each pair points
|
||||
// to the exact same pin.
|
||||
D0, TX gpio.PinIO // Transmit; SPI_MOSI
|
||||
D1, RX gpio.PinIO // Receive; SPI_MISO
|
||||
D2, RTS gpio.PinIO // Request To Send Control Output / Handshake signal; SPI_CLK
|
||||
D3, CTS gpio.PinIO // Clear to Send Control input / Handshake signal; SPI_CS
|
||||
D4, DTR gpio.PinIO // Data Terminal Ready Control Output / Handshake signal
|
||||
D5, DSR gpio.PinIO // Data Set Ready Control Input / Handshake signal
|
||||
D6, DCD gpio.PinIO // Data Carrier Detect Control input
|
||||
D7, RI gpio.PinIO // Ring Indicator Control Input. When remote wake up is enabled in the internal EEPROM taking RI# low can be used to resume the PC USB host controller from suspend.
|
||||
|
||||
// The CBus pins are slower to use, but can drive an high load, like a LED.
|
||||
C0 gpio.PinIO
|
||||
C1 gpio.PinIO
|
||||
C2 gpio.PinIO
|
||||
C3 gpio.PinIO
|
||||
|
||||
dbus [8]dbusPinSync
|
||||
cbus [4]cbusPin
|
||||
hdr [12]gpio.PinIO
|
||||
|
||||
// Mutable.
|
||||
mu sync.Mutex
|
||||
usingSPI bool
|
||||
usingCBus bool
|
||||
s spiSyncPort
|
||||
dmask uint8 // 0 input, 1 output
|
||||
dvalue uint8
|
||||
cbusnibble uint8 // upper nibble is I/O control, lower nibble is values.
|
||||
}
|
||||
|
||||
// Header returns the GPIO pins exposed on the chip.
|
||||
func (f *FT232R) Header() []gpio.PinIO {
|
||||
out := make([]gpio.PinIO, len(f.hdr))
|
||||
copy(out, f.hdr[:])
|
||||
return out
|
||||
}
|
||||
|
||||
// SetDBusMask sets all D0~D7 input or output mode at once.
|
||||
//
|
||||
// mask is the input/output pins to use. A bit value of 0 sets the
|
||||
// corresponding pin to an input, a bit value of 1 sets the corresponding pin
|
||||
// to an output.
|
||||
//
|
||||
// It should be called before calling Tx().
|
||||
func (f *FT232R) SetDBusMask(mask uint8) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
if f.usingSPI {
|
||||
return errors.New("d2xx: already using SPI")
|
||||
}
|
||||
return f.setDBusMaskLocked(mask)
|
||||
}
|
||||
|
||||
// Tx does synchronized read-then-write on all the D0~D7 GPIOs.
|
||||
//
|
||||
// SetSpeed() determines the pace at which the I/O is done.
|
||||
//
|
||||
// SetDBusMask() determines which bits are interpreted in the w and r byte
|
||||
// slice. w has its significant value masked by 'mask' and r has its
|
||||
// significant value masked by '^mask'.
|
||||
//
|
||||
// Input sample is done *before* updating outputs. So r[0] is sampled before
|
||||
// w[0] is used. The last w byte should be duplicated if an addition read is
|
||||
// desired.
|
||||
//
|
||||
// On the Adafruit cable, only the first 4 bits D0(TX), D1(RX), D2(RTS) and
|
||||
// D3(CTS) are connected. This is just enough to create a full duplex SPI bus!
|
||||
func (f *FT232R) Tx(w, r []byte) error {
|
||||
if len(w) != 0 {
|
||||
if len(r) != 0 && len(w) != len(r) {
|
||||
return errors.New("d2xx: length of buffer w and r must match")
|
||||
}
|
||||
} else if len(r) == 0 {
|
||||
return errors.New("d2xx: at least one of w or r must be passed")
|
||||
}
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
if f.usingSPI {
|
||||
return errors.New("d2xx: already using SPI")
|
||||
}
|
||||
return f.txLocked(w, r)
|
||||
}
|
||||
|
||||
// SPI returns a SPI port over the first 4 pins.
|
||||
//
|
||||
// It uses D0(TX), D1(RX), D2(RTS) and D3(CTS). D2(RTS) is the clock, D0(TX)
|
||||
// the output (MOSI), D1(RX) is the input (MISO) and D3(CTS) is CS line.
|
||||
func (f *FT232R) SPI() (spi.PortCloser, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
if f.usingSPI {
|
||||
return nil, errors.New("d2xx: already using SPI")
|
||||
}
|
||||
// Don't mark it as being used yet. It only become used once Connect() is
|
||||
// called.
|
||||
return &f.s, nil
|
||||
}
|
||||
|
||||
// setDBusMaskLocked is the locked version of SetDBusMask.
|
||||
func (f *FT232R) setDBusMaskLocked(mask uint8) error {
|
||||
if mask != f.dmask {
|
||||
if err := f.h.SetBitMode(mask, bitModeAsyncBitbang); err != nil {
|
||||
return err
|
||||
}
|
||||
f.dmask = mask
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FT232R) txLocked(w, r []byte) error {
|
||||
// Investigate FT232R clock issue:
|
||||
// http://developer.intra2net.com/mailarchive/html/libftdi/2010/msg00240.html
|
||||
|
||||
// The FT232R has 128 bytes TX buffer and 256 bytes RX buffer. Chunk into 64
|
||||
// bytes chunks. That's half the buffer size of the TX buffer and permits
|
||||
// pipelining and removes the risk of buffer overrun. This is important
|
||||
// otherwise there's huge gaps due to the USB transmit overhead.
|
||||
// TODO(maruel): Determine what's optimal via experimentation.
|
||||
chunk := 64
|
||||
var scratch [128]byte
|
||||
if len(w) == 0 {
|
||||
// Read only.
|
||||
for i := range scratch {
|
||||
scratch[i] = f.dvalue
|
||||
}
|
||||
for len(r) != 0 {
|
||||
// TODO(maruel): Optimize.
|
||||
c := len(r)
|
||||
if c > chunk {
|
||||
c = chunk
|
||||
}
|
||||
if _, err := f.h.Write(scratch[:c]); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := f.h.ReadAll(context.Background(), r[:c]); err != nil {
|
||||
return err
|
||||
}
|
||||
r = r[c:]
|
||||
}
|
||||
} else if len(r) == 0 {
|
||||
// Write only.
|
||||
// The first write is 128 bytes to fill the buffer.
|
||||
chunk = 128
|
||||
for len(w) != 0 {
|
||||
c := len(w)
|
||||
if c > chunk {
|
||||
c = chunk
|
||||
}
|
||||
if _, err := f.h.Write(w[:c]); err != nil {
|
||||
return err
|
||||
}
|
||||
w = w[c:]
|
||||
chunk = 64
|
||||
}
|
||||
/*
|
||||
// Let the USB drive pace it.
|
||||
if _, err := f.h.Write(w); err != nil {
|
||||
return err
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
// R/W.
|
||||
// Always write one 'w' ahead.
|
||||
// The first write is 128 bytes to fill the buffer.
|
||||
chunk = 128
|
||||
cw := len(w)
|
||||
if cw > chunk {
|
||||
cw = chunk
|
||||
}
|
||||
if _, err := f.h.Write(w[:cw]); err != nil {
|
||||
return err
|
||||
}
|
||||
w = w[cw:]
|
||||
chunk = 64
|
||||
for len(r) != 0 {
|
||||
// Read then write.
|
||||
cr := len(r)
|
||||
if cr > chunk {
|
||||
cr = chunk
|
||||
}
|
||||
if _, err := f.h.ReadAll(context.Background(), r[:cr]); err != nil {
|
||||
return err
|
||||
}
|
||||
r = r[cr:]
|
||||
|
||||
cw = len(w)
|
||||
if cw > chunk {
|
||||
cw = chunk
|
||||
}
|
||||
if cw != 0 {
|
||||
if _, err := f.h.Write(w[:cw]); err != nil {
|
||||
return err
|
||||
}
|
||||
w = w[cw:]
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// dbusSyncGPIOFunc implements dbusSync. It returns the function of a GPIO
|
||||
// pin.
|
||||
func (f *FT232R) dbusSyncGPIOFunc(n int) string {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
if f.usingSPI {
|
||||
switch n {
|
||||
case 0:
|
||||
return "SPI_MOSI" // TX
|
||||
case 1:
|
||||
return "SPI_MISO" // RX
|
||||
case 2:
|
||||
return "SPI_CLK" // RTS
|
||||
case 3:
|
||||
return "SPI_CS" // CTS
|
||||
}
|
||||
}
|
||||
mask := uint8(1 << uint(n))
|
||||
if f.dmask&mask != 0 {
|
||||
return "Out/" + gpio.Level(f.dvalue&mask != 0).String()
|
||||
}
|
||||
return "In/" + f.dbusSyncReadLocked(n).String()
|
||||
}
|
||||
|
||||
// dbusSyncGPIOIn implements dbusSync.
|
||||
func (f *FT232R) dbusSyncGPIOIn(n int) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
// TODO(maruel): if f.usingSPI && n < 4.
|
||||
mask := uint8(1 << uint(n))
|
||||
if f.dmask&mask == 0 {
|
||||
// Already input.
|
||||
return nil
|
||||
}
|
||||
v := f.dmask &^ mask
|
||||
if err := f.h.SetBitMode(v, bitModeAsyncBitbang); err != nil {
|
||||
return err
|
||||
}
|
||||
f.dmask = v
|
||||
return nil
|
||||
}
|
||||
|
||||
// dbusSyncGPIORead implements dbusSync.
|
||||
func (f *FT232R) dbusSyncGPIORead(n int) gpio.Level {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.dbusSyncReadLocked(n)
|
||||
}
|
||||
|
||||
func (f *FT232R) dbusSyncReadLocked(n int) gpio.Level {
|
||||
// In synchronous mode, to read we must write first to for a sample.
|
||||
b := [1]byte{f.dvalue}
|
||||
if _, err := f.h.Write(b[:]); err != nil {
|
||||
return gpio.Low
|
||||
}
|
||||
mask := uint8(1 << uint(n))
|
||||
if _, err := f.h.ReadAll(context.Background(), b[:]); err != nil {
|
||||
return gpio.Low
|
||||
}
|
||||
f.dvalue = b[0]
|
||||
return f.dvalue&mask != 0
|
||||
}
|
||||
|
||||
// dbusSyncGPIOOut implements dbusSync.
|
||||
func (f *FT232R) dbusSyncGPIOOut(n int, l gpio.Level) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
mask := uint8(1 << uint(n))
|
||||
if f.dmask&mask != 1 {
|
||||
// Was input.
|
||||
v := f.dmask | mask
|
||||
if err := f.h.SetBitMode(v, bitModeAsyncBitbang); err != nil {
|
||||
return err
|
||||
}
|
||||
f.dmask = v
|
||||
}
|
||||
return f.dbusSyncGPIOOutLocked(n, l)
|
||||
}
|
||||
|
||||
func (f *FT232R) dbusSyncGPIOOutLocked(n int, l gpio.Level) error {
|
||||
b := [1]byte{f.dvalue}
|
||||
if _, err := f.h.Write(b[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
f.dvalue = b[0]
|
||||
// In synchronous mode, we must read after writing to flush the buffer.
|
||||
if _, err := f.h.Write(b[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cBusGPIOFunc implements cBusGPIO.
|
||||
func (f *FT232R) cBusGPIOFunc(n int) string {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
fmask := uint8(0x10 << uint(n))
|
||||
vmask := uint8(1 << uint(n))
|
||||
if f.cbusnibble&fmask != 0 {
|
||||
return "Out/" + gpio.Level(f.cbusnibble&vmask != 0).String()
|
||||
}
|
||||
return "In/" + f.cBusReadLocked(n).String()
|
||||
}
|
||||
|
||||
// cBusGPIOIn implements cBusGPIO.
|
||||
func (f *FT232R) cBusGPIOIn(n int) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
fmask := uint8(0x10 << uint(n))
|
||||
if f.cbusnibble&fmask == 0 {
|
||||
// Already input.
|
||||
return nil
|
||||
}
|
||||
v := f.cbusnibble &^ fmask
|
||||
if err := f.h.SetBitMode(v, bitModeCbusBitbang); err != nil {
|
||||
return err
|
||||
}
|
||||
f.cbusnibble = v
|
||||
return nil
|
||||
}
|
||||
|
||||
// cBusGPIORead implements cBusGPIO.
|
||||
func (f *FT232R) cBusGPIORead(n int) gpio.Level {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.cBusReadLocked(n)
|
||||
}
|
||||
|
||||
func (f *FT232R) cBusReadLocked(n int) gpio.Level {
|
||||
v, err := f.h.GetBitMode()
|
||||
if err != nil {
|
||||
return gpio.Low
|
||||
}
|
||||
f.cbusnibble = v
|
||||
vmask := uint8(1 << uint(n))
|
||||
return f.cbusnibble&vmask != 0
|
||||
}
|
||||
|
||||
// cBusGPIOOut implements cBusGPIO.
|
||||
func (f *FT232R) cBusGPIOOut(n int, l gpio.Level) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
fmask := uint8(0x10 << uint(n))
|
||||
vmask := uint8(1 << uint(n))
|
||||
v := f.cbusnibble | fmask
|
||||
if l {
|
||||
v |= vmask
|
||||
} else {
|
||||
v &^= vmask
|
||||
}
|
||||
if f.cbusnibble == v {
|
||||
// Was already in the right mode.
|
||||
return nil
|
||||
}
|
||||
if err := f.h.SetBitMode(v, bitModeCbusBitbang); err != nil {
|
||||
return err
|
||||
}
|
||||
f.cbusnibble = v
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var _ conn.Resource = Dev(nil)
|
20
vendor/periph.io/x/host/v3/ftdi/doc.go
generated
vendored
Normal file
20
vendor/periph.io/x/host/v3/ftdi/doc.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package ftdi implements support for popular FTDI devices.
|
||||
//
|
||||
// The supported devices (FT232h/FT232r) implement support for various
|
||||
// protocols like the GPIO, I²C, SPI, UART, JTAG.
|
||||
//
|
||||
// More details
|
||||
//
|
||||
// See https://periph.io/device/ftdi/ for more details, and how to configure
|
||||
// the host to be able to use this driver.
|
||||
//
|
||||
// Datasheets
|
||||
//
|
||||
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232R.pdf
|
||||
//
|
||||
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232H.pdf
|
||||
package ftdi
|
213
vendor/periph.io/x/host/v3/ftdi/driver.go
generated
vendored
Normal file
213
vendor/periph.io/x/host/v3/ftdi/driver.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package ftdi
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"periph.io/x/conn/v3/driver/driverreg"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/gpio/gpioreg"
|
||||
"periph.io/x/conn/v3/i2c"
|
||||
"periph.io/x/conn/v3/i2c/i2creg"
|
||||
"periph.io/x/conn/v3/pin"
|
||||
"periph.io/x/conn/v3/pin/pinreg"
|
||||
"periph.io/x/conn/v3/spi/spireg"
|
||||
"periph.io/x/d2xx"
|
||||
)
|
||||
|
||||
// All enumerates all the connected FTDI devices.
|
||||
func All() []Dev {
|
||||
drv.mu.Lock()
|
||||
defer drv.mu.Unlock()
|
||||
out := make([]Dev, len(drv.all))
|
||||
copy(out, drv.all)
|
||||
return out
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// open opens a FTDI device.
|
||||
//
|
||||
// Must be called with mu held.
|
||||
func open(opener func(i int) (d2xx.Handle, d2xx.Err), i int) (Dev, error) {
|
||||
h, err := openHandle(opener, i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := h.Init(); err != nil {
|
||||
// setupCommon() takes the device in its previous state. It could be in an
|
||||
// unexpected state, so try resetting it first.
|
||||
if err := h.Reset(); err != nil {
|
||||
_ = h.Close()
|
||||
return nil, err
|
||||
}
|
||||
if err := h.Init(); err != nil {
|
||||
_ = h.Close()
|
||||
return nil, err
|
||||
}
|
||||
// The second attempt worked.
|
||||
}
|
||||
// Makes a copy of the handle.
|
||||
g := generic{index: i, h: h, name: h.t.String()}
|
||||
if i > 0 {
|
||||
// When more than one device is present, add "(index)" suffix.
|
||||
// TODO(maruel): Using the serial number would be nicer than a number.
|
||||
g.name += "(" + strconv.Itoa(i) + ")"
|
||||
}
|
||||
// Makes a copy of the generic instance.
|
||||
switch g.h.t {
|
||||
case DevTypeFT232H:
|
||||
f, err := newFT232H(g)
|
||||
if err != nil {
|
||||
_ = h.Close()
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
case DevTypeFT2232H:
|
||||
f, err := newFT232H(g)
|
||||
if err != nil {
|
||||
_ = h.Close()
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
case DevTypeFT232R:
|
||||
f, err := newFT232R(g)
|
||||
if err != nil {
|
||||
_ = h.Close()
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
default:
|
||||
return &g, nil
|
||||
}
|
||||
}
|
||||
|
||||
// registerDev registers the header and supported buses and ports in the
|
||||
// relevant registries.
|
||||
func registerDev(d Dev, multi bool) error {
|
||||
name := d.String()
|
||||
hdr := d.Header()
|
||||
|
||||
// Register the GPIOs.
|
||||
for _, p := range hdr {
|
||||
if err := gpioreg.Register(p); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !multi {
|
||||
// Register shorthands.
|
||||
// The "." used here vs the "_" used in pinreg is unfortunate. Investigate
|
||||
// a better way.
|
||||
prefix := len(name) + 1
|
||||
for _, p := range hdr {
|
||||
n := p.Name()
|
||||
if err := gpioreg.RegisterAlias(n[prefix:], n); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register the header.
|
||||
raw := make([][]pin.Pin, len(hdr))
|
||||
for i := range hdr {
|
||||
raw[i] = []pin.Pin{hdr[i]}
|
||||
}
|
||||
if err := pinreg.Register(name, raw); err != nil {
|
||||
return err
|
||||
}
|
||||
switch t := d.(type) {
|
||||
case *FT232H:
|
||||
// Register I²C without pull up.
|
||||
if err := i2creg.Register(name, nil, -1, func() (i2c.BusCloser, error) { return t.I2C(gpio.Float) }); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := spireg.Register(name, nil, -1, t.SPI); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(maruel): UART
|
||||
case *FT232R:
|
||||
// TODO(maruel): SPI, UART
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// driver implements driver.Impl.
|
||||
type driver struct {
|
||||
mu sync.Mutex
|
||||
all []Dev
|
||||
d2xxOpen func(i int) (d2xx.Handle, d2xx.Err)
|
||||
numDevices func() (int, error)
|
||||
}
|
||||
|
||||
func (d *driver) String() string {
|
||||
return "ftdi"
|
||||
}
|
||||
|
||||
func (d *driver) Prerequisites() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) After() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) Init() (bool, error) {
|
||||
num, err := d.numDevices()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
multi := num > 1
|
||||
for i := 0; i < num; i++ {
|
||||
// TODO(maruel): Close the device one day. :)
|
||||
if dev, err1 := open(d.d2xxOpen, i); err1 == nil {
|
||||
d.all = append(d.all, dev)
|
||||
if err = registerDev(dev, multi); err != nil {
|
||||
return true, err
|
||||
}
|
||||
} else {
|
||||
// Create a shallow broken handle, so the user can learn how to fix the
|
||||
// problem.
|
||||
//
|
||||
// TODO(maruel): On macOS with a FT232R, calling two processes in a row
|
||||
// often results in a broken device on the second process. Figure out why
|
||||
// and make it more resilient.
|
||||
err = err1
|
||||
// The serial number is not available so what can be listed is limited.
|
||||
// TODO(maruel): Add VID/PID?
|
||||
name := "broken#" + strconv.Itoa(i) + ": " + err.Error()
|
||||
d.all = append(d.all, &broken{index: i, err: err, name: name})
|
||||
}
|
||||
}
|
||||
return true, err
|
||||
}
|
||||
|
||||
func (d *driver) reset() {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
d.all = nil
|
||||
// open is mocked in tests.
|
||||
d.d2xxOpen = d2xx.Open
|
||||
// numDevices is mocked in tests.
|
||||
d.numDevices = numDevices
|
||||
|
||||
// The d2xx can hang for up to the timeout under certain circumstances and the
|
||||
// Go profiler is not very useful to find the source, so use manual logging
|
||||
// to see where time it spent.
|
||||
//d.d2xxOpen = func(i int) (d2xx.Handle, int) {
|
||||
// h, e := d2xxOpen(i)
|
||||
// return &d2xxLoggingHandle{h}, e
|
||||
//}
|
||||
}
|
||||
|
||||
func init() {
|
||||
if d2xx.Available {
|
||||
drv.reset()
|
||||
driverreg.MustRegister(&drv)
|
||||
}
|
||||
}
|
||||
|
||||
var drv driver
|
368
vendor/periph.io/x/host/v3/ftdi/eeprom.go
generated
vendored
Normal file
368
vendor/periph.io/x/host/v3/ftdi/eeprom.go
generated
vendored
Normal file
@ -0,0 +1,368 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package ftdi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// EEPROM is the unprocessed EEPROM content.
|
||||
//
|
||||
// The EEPROM is in 3 parts: the defined struct, the 4 strings and the rest
|
||||
// which is used as an 'user area'. The size of the user area depends on the
|
||||
// length of the strings. The user area content is not included in this struct.
|
||||
type EEPROM struct {
|
||||
// Raw is the raw EEPROM content. It excludes the strings.
|
||||
Raw []byte
|
||||
|
||||
// The following condition must be true: len(Manufacturer) + len(Desc) <= 40.
|
||||
Manufacturer string
|
||||
ManufacturerID string
|
||||
Desc string
|
||||
Serial string
|
||||
}
|
||||
|
||||
// Validate checks that the data is good.
|
||||
func (e *EEPROM) Validate() error {
|
||||
// Verify that the values are set correctly.
|
||||
if len(e.Manufacturer) > 40 {
|
||||
return errors.New("ftdi: Manufacturer is too long")
|
||||
}
|
||||
if len(e.ManufacturerID) > 40 {
|
||||
return errors.New("ftdi: ManufacturerID is too long")
|
||||
}
|
||||
if len(e.Desc) > 40 {
|
||||
return errors.New("ftdi: Desc is too long")
|
||||
}
|
||||
if len(e.Serial) > 40 {
|
||||
return errors.New("ftdi: Serial is too long")
|
||||
}
|
||||
if len(e.Manufacturer)+len(e.Desc) > 40 {
|
||||
return errors.New("ftdi: length of Manufacturer plus Desc is too long")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EEPROM) AsHeader() *EEPROMHeader {
|
||||
// sizeof(EEPROMHeader)
|
||||
if len(e.Raw) < 16 {
|
||||
return nil
|
||||
}
|
||||
return (*EEPROMHeader)(unsafe.Pointer(&e.Raw[0]))
|
||||
}
|
||||
|
||||
// AsFT232H returns the Raw data aliased as EEPROMFT232H.
|
||||
func (e *EEPROM) AsFT232H() *EEPROMFT232H {
|
||||
// sizeof(EEPROMFT232H)
|
||||
if len(e.Raw) < 44 {
|
||||
return nil
|
||||
}
|
||||
return (*EEPROMFT232H)(unsafe.Pointer(&e.Raw[0]))
|
||||
}
|
||||
|
||||
// AsFT2232H returns the Raw data aliased as EEPROMFT2232H.
|
||||
func (e *EEPROM) AsFT2232H() *EEPROMFT2232H {
|
||||
// sizeof(EEPROMFT2232H)
|
||||
if len(e.Raw) < 40 {
|
||||
return nil
|
||||
}
|
||||
return (*EEPROMFT2232H)(unsafe.Pointer(&e.Raw[0]))
|
||||
}
|
||||
|
||||
// AsFT232R returns the Raw data aliased as EEPROMFT232R.
|
||||
func (e *EEPROM) AsFT232R() *EEPROMFT232R {
|
||||
// sizeof(EEPROMFT232R)
|
||||
if len(e.Raw) < 32 {
|
||||
return nil
|
||||
}
|
||||
return (*EEPROMFT232R)(unsafe.Pointer(&e.Raw[0]))
|
||||
}
|
||||
|
||||
// FT232hCBusMux is stored in the FT232H EEPROM to control each CBus pin.
|
||||
type FT232hCBusMux uint8
|
||||
|
||||
const (
|
||||
// TriSt-PU; Sets in Tristate (pull up) (C0~C6, C8, C9) on 75kΩ.
|
||||
FT232hCBusTristatePullUp FT232hCBusMux = 0x00
|
||||
// TXLED#; Pulses low when transmitting data (C0~C6, C8, C9).
|
||||
FT232hCBusTxLED FT232hCBusMux = 0x01
|
||||
// RXLED#; Pulses low when receiving data (C0~C6, C8, C9).
|
||||
FT232hCBusRxLED FT232hCBusMux = 0x02
|
||||
// TX&RXLED#; Pulses low when either receiving or transmitting data (C0~C6,
|
||||
// C8, C9).
|
||||
FT232hCBusTxRxLED FT232hCBusMux = 0x03
|
||||
// PWREN#; Output is low after the device has been configured by USB, then
|
||||
// high during USB suspend mode (C0~C6, C8, C9).
|
||||
//
|
||||
// Must be used with an external 10kΩ pull up.
|
||||
FT232hCBusPwrEnable FT232hCBusMux = 0x04
|
||||
// SLEEP#; Goes low during USB suspend mode (C0~C6, C8, C9).
|
||||
FT232hCBusSleep FT232hCBusMux = 0x05
|
||||
// DRIVE1; Drives pin to logic 0 (C0~C6, C8, C9).
|
||||
FT232hCBusDrive0 FT232hCBusMux = 0x06
|
||||
// DRIVE1; Drives pin to logic 1 (C0, C5, C6, C8, C9).
|
||||
FT232hCBusDrive1 FT232hCBusMux = 0x07
|
||||
// I/O Mode; CBus bit-bang mode option (C5, C6, C8, C9).
|
||||
FT232hCBusIOMode FT232hCBusMux = 0x08
|
||||
// TXDEN; Tx Data Enable. Used with RS485 level converters to enable the line
|
||||
// driver during data transmit. It is active one bit time before the start
|
||||
// bit up to until the end of the stop bit (C0~C6, C8, C9).
|
||||
FT232hCBusTxdEnable FT232hCBusMux = 0x09
|
||||
// CLK30 30MHz clock output (C0, C5, C6, C8, C9).
|
||||
FT232hCBusClk30 FT232hCBusMux = 0x0A
|
||||
// CLK15 15MHz clock output (C0, C5, C6, C8, C9).
|
||||
FT232hCBusClk15 FT232hCBusMux = 0x0B
|
||||
// CLK7.5 7.5MHz clock output (C0, C5, C6, C8, C9).
|
||||
FT232hCBusClk7_5 FT232hCBusMux = 0x0C
|
||||
)
|
||||
|
||||
const ft232hCBusMuxName = "FT232hCBusTristatePullUpFT232hCBusTxLEDFT232hCBusRxLEDFT232hCBusTxRxLEDFT232hCBusPwrEnableFT232hCBusSleepFT232hCBusDrive0FT232hCBusDrive1FT232hCBusIOModeFT232hCBusTxdEnableFT232hCBusClk30FT232hCBusClk15FT232hCBusClk7_5"
|
||||
|
||||
var fr232hCBusMuxIndex = [...]uint8{0, 24, 39, 54, 71, 90, 105, 121, 137, 153, 172, 187, 202, 218}
|
||||
|
||||
func (f FT232hCBusMux) String() string {
|
||||
if f >= FT232hCBusMux(len(fr232hCBusMuxIndex)-1) {
|
||||
return fmt.Sprintf("FT232hCBusMux(%d)", f)
|
||||
}
|
||||
return ft232hCBusMuxName[fr232hCBusMuxIndex[f]:fr232hCBusMuxIndex[f+1]]
|
||||
}
|
||||
|
||||
// FT232rCBusMux is stored in the FT232R EEPROM to control each CBus pin.
|
||||
type FT232rCBusMux uint8
|
||||
|
||||
const (
|
||||
// TXDEN; Tx Data Enable. Used with RS485 level converters to enable the line
|
||||
// driver during data transmit. It is active one bit time before the start
|
||||
// bit up to until the end of the stop bit (C0~C4).
|
||||
FT232rCBusTxdEnable FT232rCBusMux = 0x00
|
||||
// PWREN#; Output is low after the device has been configured by USB, then
|
||||
// high during USB suspend mode (C0~C4).
|
||||
//
|
||||
// Must be used with an external 10kΩ pull up.
|
||||
FT232rCBusPwrEnable FT232rCBusMux = 0x01
|
||||
// RXLED#; Pulses low when receiving data (C0~C4).
|
||||
FT232rCBusRxLED FT232rCBusMux = 0x02
|
||||
// TXLED#; Pulses low when transmitting data (C0~C4).
|
||||
FT232rCBusTxLED FT232rCBusMux = 0x03
|
||||
// TX&RXLED#; Pulses low when either receiving or transmitting data (C0~C4).
|
||||
FT232rCBusTxRxLED FT232rCBusMux = 0x04
|
||||
// SLEEP# Goes low during USB suspend mode (C0~C4).
|
||||
FT232rCBusSleep FT232rCBusMux = 0x05
|
||||
// CLK48 48Mhz +/-0.7% clock output (C0~C4).
|
||||
FT232rCBusClk48 FT232rCBusMux = 0x06
|
||||
// CLK24 24Mhz clock output (C0~C4).
|
||||
FT232rCBusClk24 FT232rCBusMux = 0x07
|
||||
// CLK12 12Mhz clock output (C0~C4).
|
||||
FT232rCBusClk12 FT232rCBusMux = 0x08
|
||||
// CLK6 6Mhz +/-0.7% clock output (C0~C4).
|
||||
FT232rCBusClk6 FT232rCBusMux = 0x09
|
||||
// CBitBangI/O; CBus bit-bang mode option (C0~C3).
|
||||
FT232rCBusIOMode FT232rCBusMux = 0x0A
|
||||
// BitBangWRn; CBus WR# strobe output (C0~C3).
|
||||
FT232rCBusBitBangWR FT232rCBusMux = 0x0B
|
||||
// BitBangRDn; CBus RD# strobe output (C0~C3).
|
||||
FT232rCBusBitBangRD FT232rCBusMux = 0x0C
|
||||
)
|
||||
|
||||
const ft232rCBusMuxName = "FT232rCBusTxdEnableFT232rCBusPwrEnableFT232rCBusRxLEDFT232rCBusTxLEDFT232rCBusTxRxLEDFT232rCBusSleepFT232rCBusClk48FT232rCBusClk24FT232rCBusClk12FT232rCBusClk6FT232rCBusIOModeFT232rCBusBitBangWRFT232rCBusBitBangRD"
|
||||
|
||||
var ft232rCBusMuxIndex = [...]uint8{0, 19, 38, 53, 68, 85, 100, 115, 130, 145, 159, 175, 194, 213}
|
||||
|
||||
func (f FT232rCBusMux) String() string {
|
||||
if f >= FT232rCBusMux(len(ft232rCBusMuxIndex)-1) {
|
||||
return fmt.Sprintf("FT232rCBusMux(%d)", f)
|
||||
}
|
||||
return ft232rCBusMuxName[ft232rCBusMuxIndex[f]:ft232rCBusMuxIndex[f+1]]
|
||||
}
|
||||
|
||||
// EEPROMHeader is the common header found on FTDI devices.
|
||||
//
|
||||
// It is 16 bytes long.
|
||||
type EEPROMHeader struct {
|
||||
DeviceType DevType // 0x00 FTxxxx device type to be programmed
|
||||
VendorID uint16 // 0x04 Defaults to 0x0403; can be changed
|
||||
ProductID uint16 // 0x06 Defaults to 0x6001 for FT232R, 0x6014 for FT232H, relevant value
|
||||
SerNumEnable uint8 // 0x07 bool Non-zero if serial number to be used
|
||||
Unused0 uint8 // 0x08 For alignment.
|
||||
MaxPower uint16 // 0x0A 0mA < MaxPower <= 500mA
|
||||
SelfPowered uint8 // 0x0C bool 0 = bus powered, 1 = self powered
|
||||
RemoteWakeup uint8 // 0x0D bool 0 = not capable, 1 = capable; RI# low will wake host in 20ms.
|
||||
PullDownEnable uint8 // 0x0E bool Non zero if pull down in suspend enabled
|
||||
Unused1 uint8 // 0x0F For alignment.
|
||||
}
|
||||
|
||||
// EEPROMFT232H is the EEPROM layout of a FT232H device.
|
||||
//
|
||||
// It is 44 bytes long.
|
||||
type EEPROMFT232H struct {
|
||||
EEPROMHeader
|
||||
|
||||
// FT232H specific.
|
||||
ACSlowSlew uint8 // 0x10 bool Non-zero if AC bus pins have slow slew
|
||||
ACSchmittInput uint8 // 0x11 bool Non-zero if AC bus pins are Schmitt input
|
||||
ACDriveCurrent uint8 // 0x12 Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
|
||||
ADSlowSlew uint8 // 0x13 bool Non-zero if AD bus pins have slow slew
|
||||
ADSchmittInput uint8 // 0x14 bool Non-zero if AD bus pins are Schmitt input
|
||||
ADDriveCurrent uint8 // 0x15 Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
|
||||
Cbus0 FT232hCBusMux // 0x16
|
||||
Cbus1 FT232hCBusMux // 0x17
|
||||
Cbus2 FT232hCBusMux // 0x18
|
||||
Cbus3 FT232hCBusMux // 0x19
|
||||
Cbus4 FT232hCBusMux // 0x1A
|
||||
Cbus5 FT232hCBusMux // 0x1B
|
||||
Cbus6 FT232hCBusMux // 0x1C
|
||||
Cbus7 FT232hCBusMux // 0x1D C7 is limited a sit can only do 'suspend on C7 low'. Defaults pull down.
|
||||
Cbus8 FT232hCBusMux // 0x1E
|
||||
Cbus9 FT232hCBusMux // 0x1F
|
||||
FT1248Cpol uint8 // 0x20 bool FT1248 clock polarity - clock idle high (true) or clock idle low (false)
|
||||
FT1248Lsb uint8 // 0x21 bool FT1248 data is LSB (true), or MSB (false)
|
||||
FT1248FlowControl uint8 // 0x22 bool FT1248 flow control enable
|
||||
IsFifo uint8 // 0x23 bool Non-zero if Interface is 245 FIFO
|
||||
IsFifoTar uint8 // 0x24 bool Non-zero if Interface is 245 FIFO CPU target
|
||||
IsFastSer uint8 // 0x25 bool Non-zero if Interface is Fast serial
|
||||
IsFT1248 uint8 // 0x26 bool Non-zero if Interface is FT1248
|
||||
PowerSaveEnable uint8 // 0x27 bool Suspect on ACBus7 low.
|
||||
DriverType uint8 // 0x28 bool 0 is D2XX, 1 is VCP
|
||||
Unused2 uint8 // 0x29
|
||||
Unused3 uint16 // 0x30
|
||||
}
|
||||
|
||||
func (e *EEPROMFT232H) Defaults() {
|
||||
// As found on Adafruit device.
|
||||
e.ACDriveCurrent = 4
|
||||
e.ADDriveCurrent = 4
|
||||
e.Cbus0 = FT232hCBusTristatePullUp
|
||||
e.Cbus1 = FT232hCBusTristatePullUp
|
||||
e.Cbus2 = FT232hCBusTristatePullUp
|
||||
e.Cbus3 = FT232hCBusTristatePullUp
|
||||
e.Cbus4 = FT232hCBusTristatePullUp
|
||||
e.Cbus5 = FT232hCBusTristatePullUp
|
||||
e.Cbus6 = FT232hCBusTristatePullUp
|
||||
e.Cbus7 = FT232hCBusTristatePullUp
|
||||
e.Cbus8 = FT232hCBusDrive1
|
||||
e.Cbus9 = FT232hCBusDrive0
|
||||
}
|
||||
|
||||
// EEPROMFT2232H is the EEPROM layout of a FT2232H device.
|
||||
//
|
||||
// It is 40 bytes long.
|
||||
type EEPROMFT2232H struct {
|
||||
EEPROMHeader
|
||||
|
||||
// FT232H specific.
|
||||
ALSlowSlew uint8 // 0x10 bool non-zero if AL pins have slow slew
|
||||
ALSchmittInput uint8 // 0x11 bool non-zero if AL pins are Schmitt input
|
||||
ALDriveCurrent uint8 // 0x12 Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
|
||||
AHSlowSlew uint8 // 0x13 bool non-zero if AH pins have slow slew
|
||||
AHSchmittInput uint8 // 0x14 bool non-zero if AH pins are Schmitt input
|
||||
AHDriveCurrent uint8 // 0x15 Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
|
||||
BLSlowSlew uint8 // 0x16 bool non-zero if BL pins have slow slew
|
||||
BLSchmittInput uint8 // 0x17 bool non-zero if BL pins are Schmitt input
|
||||
BLDriveCurrent uint8 // 0x18 Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
|
||||
BHSlowSlew uint8 // 0x19 bool non-zero if BH pins have slow slew
|
||||
BHSchmittInput uint8 // 0x1A bool non-zero if BH pins are Schmitt input
|
||||
BHDriveCurrent uint8 // 0x1B Valid values are 4mA, 8mA, 12mA, 16mA in 2mA units
|
||||
AIsFifo uint8 // 0x1C bool non-zero if interface is 245 FIFO
|
||||
AIsFifoTar uint8 // 0x1D bool non-zero if interface is 245 FIFO CPU target
|
||||
AIsFastSer uint8 // 0x1E bool non-zero if interface is Fast serial
|
||||
BIsFifo uint8 // 0x1F bool non-zero if interface is 245 FIFO
|
||||
BIsFifoTar uint8 // 0x20 bool non-zero if interface is 245 FIFO CPU target
|
||||
BIsFastSer uint8 // 0x21 bool non-zero if interface is Fast serial
|
||||
PowerSaveEnable uint8 // 0x22 bool non-zero if using BCBUS7 to save power for self-powered designs
|
||||
ADriverType uint8 // 0x23 bool
|
||||
BDriverType uint8 // 0x24 bool
|
||||
Unused2 uint8 // 0x25
|
||||
Unused3 uint16 // 0x26
|
||||
}
|
||||
|
||||
// EEPROMFT232R is the EEPROM layout of a FT232R device.
|
||||
//
|
||||
// It is 32 bytes long.
|
||||
type EEPROMFT232R struct {
|
||||
EEPROMHeader
|
||||
|
||||
// FT232R specific.
|
||||
IsHighCurrent uint8 // 0x10 bool High Drive I/Os; 3mA instead of 1mA (@3.3V)
|
||||
UseExtOsc uint8 // 0x11 bool Use external oscillator
|
||||
InvertTXD uint8 // 0x12 bool
|
||||
InvertRXD uint8 // 0x13 bool
|
||||
InvertRTS uint8 // 0x14 bool
|
||||
InvertCTS uint8 // 0x15 bool
|
||||
InvertDTR uint8 // 0x16 bool
|
||||
InvertDSR uint8 // 0x17 bool
|
||||
InvertDCD uint8 // 0x18 bool
|
||||
InvertRI uint8 // 0x19 bool
|
||||
Cbus0 FT232rCBusMux // 0x1A Default ft232rCBusTxLED
|
||||
Cbus1 FT232rCBusMux // 0x1B Default ft232rCBusRxLED
|
||||
Cbus2 FT232rCBusMux // 0x1C Default ft232rCBusTxdEnable
|
||||
Cbus3 FT232rCBusMux // 0x1D Default ft232rCBusPwrEnable
|
||||
Cbus4 FT232rCBusMux // 0x1E Default ft232rCBusSleep
|
||||
DriverType uint8 // 0x1F bool 0 is D2XX, 1 is VCP
|
||||
}
|
||||
|
||||
func (e *EEPROMFT232R) Defaults() {
|
||||
// As found on Adafruit device.
|
||||
e.Cbus0 = FT232rCBusTxLED
|
||||
e.Cbus1 = FT232rCBusRxLED
|
||||
e.Cbus2 = FT232rCBusTxdEnable
|
||||
e.Cbus3 = FT232rCBusPwrEnable
|
||||
e.Cbus4 = FT232rCBusSleep
|
||||
e.DriverType = 1
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// DevType is the FTDI device type.
|
||||
type DevType uint32
|
||||
|
||||
const (
|
||||
DevTypeFTBM DevType = iota // 0
|
||||
DevTypeFTAM
|
||||
DevTypeFT100AX
|
||||
DevTypeUnknown // 3
|
||||
DevTypeFT2232C
|
||||
DevTypeFT232R // 5
|
||||
DevTypeFT2232H
|
||||
DevTypeFT4232H
|
||||
DevTypeFT232H // 8
|
||||
DevTypeFTXSeries
|
||||
DevTypeFT4222H0
|
||||
DevTypeFT4222H1_2
|
||||
DevTypeFT4222H3
|
||||
DevTypeFT4222Prog
|
||||
DevTypeFT900
|
||||
DevTypeFT930
|
||||
DevTypeFTUMFTPD3A
|
||||
)
|
||||
|
||||
// EEPROMSize returns the size of the EEPROM for this device.
|
||||
func (d DevType) EEPROMSize() int {
|
||||
switch d {
|
||||
case DevTypeFT232H:
|
||||
// sizeof(EEPROMFT232H)
|
||||
return 44
|
||||
case DevTypeFT2232H:
|
||||
// sizeof(EEPROMFT2232H)
|
||||
return 40
|
||||
case DevTypeFT232R:
|
||||
// sizeof(EEPROMFT232R)
|
||||
return 32
|
||||
default:
|
||||
return 256
|
||||
}
|
||||
}
|
||||
|
||||
const devTypeName = "FTBMFTAMFT100AXUnknownFT2232CFT232RFT2232HFT4232HFT232HFTXSeriesFT4222H0FT4222H1/2FT4222H3FT4222ProgFT900FT930FTUMFTPD3A"
|
||||
|
||||
var devTypeIndex = [...]uint8{0, 4, 8, 15, 22, 29, 35, 42, 49, 55, 64, 72, 82, 90, 100, 105, 110, 120}
|
||||
|
||||
func (d DevType) String() string {
|
||||
if d >= DevType(len(devTypeIndex)-1) {
|
||||
d = DevTypeUnknown
|
||||
}
|
||||
return devTypeName[devTypeIndex[d]:devTypeIndex[d+1]]
|
||||
}
|
5
vendor/periph.io/x/host/v3/ftdi/ftdi.go
generated
vendored
Normal file
5
vendor/periph.io/x/host/v3/ftdi/ftdi.go
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
// Copyright 2021 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package ftdi
|
241
vendor/periph.io/x/host/v3/ftdi/gpio.go
generated
vendored
Normal file
241
vendor/periph.io/x/host/v3/ftdi/gpio.go
generated
vendored
Normal file
@ -0,0 +1,241 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Emulate independent GPIOs.
|
||||
|
||||
package ftdi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/physic"
|
||||
)
|
||||
|
||||
// dbusSync is the handler of a synchronous bitbang on DBus.
|
||||
//
|
||||
// More details at:
|
||||
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_232R-01_Bit_Bang_Mode_Available_For_FT232R_and_Ft245R.pdf
|
||||
type dbusSync interface {
|
||||
dbusSyncGPIOFunc(n int) string
|
||||
dbusSyncGPIOIn(n int) error
|
||||
dbusSyncGPIORead(n int) gpio.Level
|
||||
dbusSyncGPIOOut(n int, l gpio.Level) error
|
||||
}
|
||||
|
||||
// dbusPinSync represents a GPIO on a synchronous bitbang DBus.
|
||||
//
|
||||
// It is immutable and stateless.
|
||||
type dbusPinSync struct {
|
||||
n string
|
||||
num int
|
||||
bus dbusSync
|
||||
}
|
||||
|
||||
// String implements conn.Resource.
|
||||
func (s *dbusPinSync) String() string {
|
||||
return s.n
|
||||
}
|
||||
|
||||
// Halt implements conn.Resource.
|
||||
func (s *dbusPinSync) Halt() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name implements pin.Pin.
|
||||
func (s *dbusPinSync) Name() string {
|
||||
return s.n
|
||||
}
|
||||
|
||||
// Number implements pin.Pin.
|
||||
func (s *dbusPinSync) Number() int {
|
||||
return s.num
|
||||
}
|
||||
|
||||
// Function implements pin.Pin.
|
||||
func (s *dbusPinSync) Function() string {
|
||||
return s.bus.dbusSyncGPIOFunc(s.num)
|
||||
}
|
||||
|
||||
// In implements gpio.PinIn.
|
||||
func (s *dbusPinSync) In(pull gpio.Pull, e gpio.Edge) error {
|
||||
if e != gpio.NoEdge {
|
||||
// We could support it on D5.
|
||||
return errors.New("d2xx: edge triggering is not supported")
|
||||
}
|
||||
if pull != gpio.PullUp && pull != gpio.PullNoChange {
|
||||
// EEPROM has a PullDownEnable flag.
|
||||
return errors.New("d2xx: pull is not supported")
|
||||
}
|
||||
return s.bus.dbusSyncGPIOIn(s.num)
|
||||
}
|
||||
|
||||
// Read implements gpio.PinIn.
|
||||
func (s *dbusPinSync) Read() gpio.Level {
|
||||
return s.bus.dbusSyncGPIORead(s.num)
|
||||
}
|
||||
|
||||
// WaitForEdge implements gpio.PinIn.
|
||||
func (s *dbusPinSync) WaitForEdge(t time.Duration) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultPull implements gpio.PinIn.
|
||||
func (s *dbusPinSync) DefaultPull() gpio.Pull {
|
||||
// 200kΩ
|
||||
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232R.pdf
|
||||
// p. 24
|
||||
return gpio.PullUp
|
||||
}
|
||||
|
||||
// Pull implements gpio.PinIn.
|
||||
func (s *dbusPinSync) Pull() gpio.Pull {
|
||||
return gpio.PullUp
|
||||
}
|
||||
|
||||
// Out implements gpio.PinOut.
|
||||
func (s *dbusPinSync) Out(l gpio.Level) error {
|
||||
return s.bus.dbusSyncGPIOOut(s.num, l)
|
||||
}
|
||||
|
||||
// PWM implements gpio.PinOut.
|
||||
func (s *dbusPinSync) PWM(d gpio.Duty, f physic.Frequency) error {
|
||||
return errors.New("d2xx: not implemented")
|
||||
}
|
||||
|
||||
/*
|
||||
func (s *dbusPinSync) Drive() physic.ElectricCurrent {
|
||||
// optionally 3
|
||||
//return s.bus.ee.DDriveCurrent * physic.MilliAmpere
|
||||
return physic.MilliAmpere
|
||||
}
|
||||
|
||||
func (s *dbusPinSync) SlewLimit() bool {
|
||||
//return s.bus.ee.DSlowSlew
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *dbusPinSync) Hysteresis() bool {
|
||||
//return s.bus.ee.DSchmittInput
|
||||
return true
|
||||
}
|
||||
*/
|
||||
|
||||
//
|
||||
|
||||
// cBusGPIO is the handler of a CBus bitbang bus.
|
||||
//
|
||||
// This is an asynchronous mode.
|
||||
//
|
||||
// More details at:
|
||||
// http://www.ftdichip.com/Support/Knowledgebase/index.html?cbusbitbangmode.htm
|
||||
type cBusGPIO interface {
|
||||
cBusGPIOFunc(n int) string
|
||||
cBusGPIOIn(n int) error
|
||||
cBusGPIORead(n int) gpio.Level
|
||||
cBusGPIOOut(n int, l gpio.Level) error
|
||||
}
|
||||
|
||||
// cbusPin represents a GPIO on a CBus bitbang bus.
|
||||
//
|
||||
// It is immutable and stateless.
|
||||
type cbusPin struct {
|
||||
n string
|
||||
num int
|
||||
p gpio.Pull
|
||||
bus cBusGPIO
|
||||
}
|
||||
|
||||
// String implements conn.Resource.
|
||||
func (c *cbusPin) String() string {
|
||||
return c.n
|
||||
}
|
||||
|
||||
// Halt implements conn.Resource.
|
||||
func (c *cbusPin) Halt() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name implements pin.Pin.
|
||||
func (c *cbusPin) Name() string {
|
||||
return c.n
|
||||
}
|
||||
|
||||
// Number implements pin.Pin.
|
||||
func (c *cbusPin) Number() int {
|
||||
return c.num
|
||||
}
|
||||
|
||||
// Function implements pin.Pin.
|
||||
func (c *cbusPin) Function() string {
|
||||
return c.bus.cBusGPIOFunc(c.num)
|
||||
}
|
||||
|
||||
// In implements gpio.PinIn.
|
||||
func (c *cbusPin) In(pull gpio.Pull, e gpio.Edge) error {
|
||||
if e != gpio.NoEdge {
|
||||
// We could support it on D5.
|
||||
return errors.New("d2xx: edge triggering is not supported")
|
||||
}
|
||||
if pull != c.p && pull != gpio.PullNoChange {
|
||||
// EEPROM has a PullDownEnable flag.
|
||||
return errors.New("d2xx: pull is not supported")
|
||||
}
|
||||
return c.bus.cBusGPIOIn(c.num)
|
||||
}
|
||||
|
||||
// Read implements gpio.PinIn.
|
||||
func (c *cbusPin) Read() gpio.Level {
|
||||
return c.bus.cBusGPIORead(c.num)
|
||||
}
|
||||
|
||||
// WaitForEdge implements gpio.PinIn.
|
||||
func (c *cbusPin) WaitForEdge(t time.Duration) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultPull implements gpio.PinIn.
|
||||
func (c *cbusPin) DefaultPull() gpio.Pull {
|
||||
// 200kΩ
|
||||
// http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232R.pdf
|
||||
// p. 24
|
||||
return c.p
|
||||
}
|
||||
|
||||
// Pull implements gpio.PinIn.
|
||||
func (c *cbusPin) Pull() gpio.Pull {
|
||||
return c.p
|
||||
}
|
||||
|
||||
// Out implements gpio.PinOut.
|
||||
func (c *cbusPin) Out(l gpio.Level) error {
|
||||
return c.bus.cBusGPIOOut(c.num, l)
|
||||
}
|
||||
|
||||
// PWM implements gpio.PinOut.
|
||||
func (c *cbusPin) PWM(d gpio.Duty, f physic.Frequency) error {
|
||||
return errors.New("d2xx: not implemented")
|
||||
}
|
||||
|
||||
/*
|
||||
func (c *cbusPin) Drive() physic.ElectricCurrent {
|
||||
// optionally 3
|
||||
//return c.bus.ee.CDriveCurrent * physic.MilliAmpere
|
||||
return physic.MilliAmpere
|
||||
}
|
||||
|
||||
func (c *cbusPin) SlewLimit() bool {
|
||||
//return c.bus.ee.CSlowSlew
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *cbusPin) Hysteresis() bool {
|
||||
//return c.bus.ee.CSchmittInput
|
||||
return true
|
||||
}
|
||||
*/
|
||||
|
||||
var _ gpio.PinIO = &dbusPinSync{}
|
||||
var _ gpio.PinIO = &cbusPin{}
|
382
vendor/periph.io/x/host/v3/ftdi/handle.go
generated
vendored
Normal file
382
vendor/periph.io/x/host/v3/ftdi/handle.go
generated
vendored
Normal file
@ -0,0 +1,382 @@
|
||||
// Copyright 2021 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package ftdi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"periph.io/x/conn/v3/physic"
|
||||
"periph.io/x/d2xx"
|
||||
)
|
||||
|
||||
//
|
||||
|
||||
// bitMode is used by SetBitMode to change the chip behavior.
|
||||
type bitMode uint8
|
||||
|
||||
const (
|
||||
// Resets all Pins to their default value
|
||||
bitModeReset bitMode = 0x00
|
||||
// Sets the DBus to asynchronous bit-bang.
|
||||
bitModeAsyncBitbang bitMode = 0x01
|
||||
// Switch to MPSSE mode (FT2232, FT2232H, FT4232H and FT232H).
|
||||
bitModeMpsse bitMode = 0x02
|
||||
// Sets the DBus to synchronous bit-bang (FT232R, FT245R, FT2232, FT2232H,
|
||||
// FT4232H and FT232H).
|
||||
bitModeSyncBitbang bitMode = 0x04
|
||||
// Switch to MCU host bus emulation (FT2232, FT2232H, FT4232H and FT232H).
|
||||
bitModeMcuHost bitMode = 0x08
|
||||
// Switch to fast opto-isolated serial mode (FT2232, FT2232H, FT4232H and
|
||||
// FT232H).
|
||||
bitModeFastSerial bitMode = 0x10
|
||||
// Sets the CBus in 4 bits bit-bang mode (FT232R and FT232H)
|
||||
// In this case, upper nibble controls which pin is output/input, lower
|
||||
// controls which of outputs are high and low.
|
||||
bitModeCbusBitbang bitMode = 0x20
|
||||
// Single Channel Synchronous 245 FIFO mode (FT2232H and FT232H).
|
||||
bitModeSyncFifo bitMode = 0x40
|
||||
)
|
||||
|
||||
// numDevices returns the number of detected devices.
|
||||
func numDevices() (int, error) {
|
||||
num, e := d2xx.CreateDeviceInfoList()
|
||||
if e != 0 {
|
||||
return 0, toErr("GetNumDevices initialization failed", e)
|
||||
}
|
||||
return num, nil
|
||||
}
|
||||
|
||||
func openHandle(opener func(i int) (d2xx.Handle, d2xx.Err), i int) (*handle, error) {
|
||||
h, e := opener(i)
|
||||
if e != 0 {
|
||||
return nil, toErr("Open", e)
|
||||
}
|
||||
d := &handle{h: h}
|
||||
t, vid, did, e := h.GetDeviceInfo()
|
||||
if e != 0 {
|
||||
_ = d.Close()
|
||||
return nil, toErr("GetDeviceInfo", e)
|
||||
}
|
||||
d.t = DevType(t)
|
||||
d.venID = vid
|
||||
d.devID = did
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// handle is a thin wrapper around the low level d2xx device handle to make it
|
||||
// more go-idiomatic.
|
||||
//
|
||||
// It also implements many utility functions to help with initialization and
|
||||
// device management.
|
||||
type handle struct {
|
||||
// It is just above 'handle' which directly maps to D2XX function calls.
|
||||
//
|
||||
// Dev converts the int error type into Go native error and handles higher
|
||||
// level functionality like reading and writing to the USB connection.
|
||||
//
|
||||
// The content of the struct is immutable after initialization.
|
||||
h d2xx.Handle
|
||||
t DevType
|
||||
venID uint16
|
||||
devID uint16
|
||||
}
|
||||
|
||||
func (h *handle) Close() error {
|
||||
// Not yet called.
|
||||
return toErr("Close", h.h.Close())
|
||||
}
|
||||
|
||||
// Init is the general setup for common devices.
|
||||
//
|
||||
// It tries first the 'happy path' which doesn't reset the device. By doing so,
|
||||
// the goal is to reduce the amount of glitches on the GPIO pins, on a best
|
||||
// effort basis. On all devices, the GPIOs are still reset as inputs, since
|
||||
// there is no way to determine if each GPIO is an input or output.
|
||||
func (h *handle) Init() error {
|
||||
// Driver: maximum packet size. Note that this clears any data in the buffer,
|
||||
// so it is good to do it immediately after a reset. The 'out' parameter is
|
||||
// ignored.
|
||||
// TODO(maruel): The FT232H doc claims a 512 byte packets support in hi-speed
|
||||
// mode, which means that this would likely be better to use this value.
|
||||
if e := h.h.SetUSBParameters(65536, 0); e != 0 {
|
||||
return toErr("SetUSBParameters", e)
|
||||
}
|
||||
// Driver: Set I/O timeouts to 15 sec. The reason is that we want the
|
||||
// timeouts to be very visible, at least as the driver is being developed.
|
||||
if e := h.h.SetTimeouts(15000, 15000); e != 0 {
|
||||
return toErr("SetTimeouts", e)
|
||||
}
|
||||
// Not sure: Disable event/error characters.
|
||||
if e := h.h.SetChars(0, false, 0, false); e != 0 {
|
||||
return toErr("SetChars", e)
|
||||
}
|
||||
// Not sure: Latency timer at 1ms.
|
||||
if e := h.h.SetLatencyTimer(1); e != 0 {
|
||||
return toErr("SetLatencyTimer", e)
|
||||
}
|
||||
// Not sure: Turn on flow control to synchronize IN requests.
|
||||
if e := h.h.SetFlowControl(); e != 0 {
|
||||
return toErr("SetFlowControl", e)
|
||||
}
|
||||
// Just in case. It's a very small cost.
|
||||
return h.Flush()
|
||||
}
|
||||
|
||||
// Reset resets the device.
|
||||
func (h *handle) Reset() error {
|
||||
if e := h.h.ResetDevice(); e != 0 {
|
||||
return toErr("Reset", e)
|
||||
}
|
||||
if err := h.SetBitMode(0, bitModeReset); err != nil {
|
||||
return err
|
||||
}
|
||||
// USB/driver: Flush any pending read buffer that had been sent by the device
|
||||
// before it reset. Do not return any error there, as the device may spew a
|
||||
// read error right after being initialized.
|
||||
_ = h.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBitMode returns the current bit mode.
|
||||
//
|
||||
// This is device-dependent.
|
||||
func (h *handle) GetBitMode() (byte, error) {
|
||||
l, e := h.h.GetBitMode()
|
||||
if e != 0 {
|
||||
return 0, toErr("GetBitMode", e)
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// SetBitMode change the mode of operation of the device.
|
||||
//
|
||||
// mask sets which pins are inputs and outputs for bitModeCbusBitbang.
|
||||
func (h *handle) SetBitMode(mask byte, mode bitMode) error {
|
||||
return toErr("SetBitMode", h.h.SetBitMode(mask, byte(mode)))
|
||||
}
|
||||
|
||||
// Flush flushes any data left in the read buffer.
|
||||
func (h *handle) Flush() error {
|
||||
var buf [128]byte
|
||||
for {
|
||||
p, err := h.Read(buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read returns as much as available in the read buffer without blocking.
|
||||
func (h *handle) Read(b []byte) (int, error) {
|
||||
// GetQueueStatus() 60µs is relatively slow compared to Read() 4µs,
|
||||
// but surprisingly if GetQueueStatus() is *not* called, Read()
|
||||
// becomes largely slower (800µs).
|
||||
//
|
||||
// TODO(maruel): This asks for more perf testing before settling on the best
|
||||
// solution.
|
||||
// TODO(maruel): Investigate FT_GetStatus().
|
||||
p, e := h.h.GetQueueStatus()
|
||||
if p == 0 || e != 0 {
|
||||
return int(p), toErr("Read/GetQueueStatus", e)
|
||||
}
|
||||
v := int(p)
|
||||
if v > len(b) {
|
||||
v = len(b)
|
||||
}
|
||||
n, e := h.h.Read(b[:v])
|
||||
return n, toErr("Read", e)
|
||||
}
|
||||
|
||||
// ReadAll blocks to return all the data.
|
||||
//
|
||||
// Similar to ioutil.ReadAll() except that it will stop if the context is
|
||||
// canceled.
|
||||
func (h *handle) ReadAll(ctx context.Context, b []byte) (int, error) {
|
||||
// TODO(maruel): Use FT_SetEventNotification() instead of looping when
|
||||
// waiting for bytes.
|
||||
for offset := 0; offset != len(b); {
|
||||
if ctx.Err() != nil {
|
||||
return offset, io.EOF
|
||||
}
|
||||
chunk := len(b) - offset
|
||||
if chunk > 4096 {
|
||||
chunk = 4096
|
||||
}
|
||||
n, err := h.Read(b[offset : offset+chunk])
|
||||
if offset += n; err != nil {
|
||||
return offset, err
|
||||
}
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// WriteFast writes to the USB device.
|
||||
//
|
||||
// In practice this takes at least 0.1ms, which limits the effective rate.
|
||||
//
|
||||
// There's no guarantee that the data is all written, so it is important to
|
||||
// check the return value.
|
||||
func (h *handle) WriteFast(b []byte) (int, error) {
|
||||
n, e := h.h.Write(b)
|
||||
return n, toErr("Write", e)
|
||||
}
|
||||
|
||||
// Write blocks until all data is written.
|
||||
func (h *handle) Write(b []byte) (int, error) {
|
||||
for offset := 0; offset != len(b); {
|
||||
chunk := len(b) - offset
|
||||
if chunk > 4096 {
|
||||
chunk = 4096
|
||||
}
|
||||
p, err := h.WriteFast(b[offset : offset+chunk])
|
||||
if err != nil {
|
||||
return offset + p, err
|
||||
}
|
||||
if p != 0 {
|
||||
offset += p
|
||||
}
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// ReadEEPROM reads the EEPROM.
|
||||
func (h *handle) ReadEEPROM(ee *EEPROM) error {
|
||||
// The raw data size must be exactly what the device contains.
|
||||
eepromSize := h.t.EEPROMSize()
|
||||
if len(ee.Raw) < eepromSize {
|
||||
ee.Raw = make([]byte, eepromSize)
|
||||
} else if len(ee.Raw) > eepromSize {
|
||||
ee.Raw = ee.Raw[:eepromSize]
|
||||
}
|
||||
ee2 := d2xx.EEPROM{Raw: ee.Raw}
|
||||
e := h.h.EEPROMRead(uint32(h.t), &ee2)
|
||||
ee.Manufacturer = ee2.Manufacturer
|
||||
ee.ManufacturerID = ee2.ManufacturerID
|
||||
ee.Desc = ee2.Desc
|
||||
ee.Serial = ee2.Serial
|
||||
if e != 0 {
|
||||
// 15 == FT_EEPROM_NOT_PROGRAMMED
|
||||
if e != 15 {
|
||||
return toErr("EEPROMRead", e)
|
||||
}
|
||||
// It's a fresh new device. Devices bought via Adafruit already have
|
||||
// their EEPROM programmed with Adafruit branding but fake devices sold by
|
||||
// CJMCU are not. Since GetDeviceInfo() above succeeded, we know the
|
||||
// device type via the USB descriptor, which is sufficient to load the
|
||||
// driver, which permits to program the EEPROM to "bootstrap" it.
|
||||
//
|
||||
// Fill it with an empty yet valid EEPROM content. We don't want to set
|
||||
// VenID or DevID to 0! Nobody would do that, right?
|
||||
ee.Raw = make([]byte, h.t.EEPROMSize())
|
||||
hdr := ee.AsHeader()
|
||||
hdr.DeviceType = h.t
|
||||
hdr.VendorID = h.venID
|
||||
hdr.ProductID = h.devID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteEEPROM programs the EEPROM.
|
||||
func (h *handle) WriteEEPROM(ee *EEPROM) error {
|
||||
if err := ee.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ee.Raw) != 0 {
|
||||
hdr := ee.AsHeader()
|
||||
if hdr == nil {
|
||||
return errors.New("ftdi: unexpected EEPROM header size")
|
||||
}
|
||||
if hdr.DeviceType != h.t {
|
||||
return errors.New("ftdi: unexpected device type set while programming EEPROM")
|
||||
}
|
||||
if hdr.VendorID != h.venID {
|
||||
return errors.New("ftdi: unexpected VenID set while programming EEPROM")
|
||||
}
|
||||
if hdr.ProductID != h.devID {
|
||||
return errors.New("ftdi: unexpected DevID set while programming EEPROM")
|
||||
}
|
||||
}
|
||||
ee2 := d2xx.EEPROM{
|
||||
Raw: ee.Raw,
|
||||
Manufacturer: ee.Manufacturer,
|
||||
ManufacturerID: ee.ManufacturerID,
|
||||
Desc: ee.Desc,
|
||||
Serial: ee.Serial,
|
||||
}
|
||||
return toErr("EEPROMWrite", h.h.EEPROMProgram(&ee2))
|
||||
}
|
||||
|
||||
// EraseEEPROM erases all the EEPROM.
|
||||
//
|
||||
// Will fail on FT232R and FT245R.
|
||||
func (h *handle) EraseEEPROM() error {
|
||||
return toErr("EraseEE", h.h.EraseEE())
|
||||
}
|
||||
|
||||
// ReadUA reads the EEPROM user area.
|
||||
//
|
||||
// May return nil when there's nothing programmed yet.
|
||||
func (h *handle) ReadUA() ([]byte, error) {
|
||||
size, e := h.h.EEUASize()
|
||||
if e != 0 {
|
||||
return nil, toErr("EEUASize", e)
|
||||
}
|
||||
if size == 0 {
|
||||
// Happens on uninitialized EEPROM.
|
||||
return nil, nil
|
||||
}
|
||||
b := make([]byte, size)
|
||||
if e := h.h.EEUARead(b); e != 0 {
|
||||
return nil, toErr("EEUARead", e)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// WriteUA writes to the EEPROM user area.
|
||||
func (h *handle) WriteUA(ua []byte) error {
|
||||
size, e := h.h.EEUASize()
|
||||
if e != 0 {
|
||||
return toErr("EEUASize", e)
|
||||
}
|
||||
if size == 0 {
|
||||
return errors.New("ftdi: please program EEPROM first")
|
||||
}
|
||||
if size < len(ua) {
|
||||
return fmt.Errorf("ftdi: maximum user area size is %d bytes", size)
|
||||
}
|
||||
if size != len(ua) {
|
||||
b := make([]byte, size)
|
||||
copy(b, ua)
|
||||
ua = b
|
||||
}
|
||||
if e := h.h.EEUAWrite(ua); e != 0 {
|
||||
return toErr("EEUAWrite", e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the baud rate.
|
||||
func (h *handle) SetBaudRate(f physic.Frequency) error {
|
||||
if f >= physic.GigaHertz {
|
||||
return errors.New("ftdi: baud rate too high")
|
||||
}
|
||||
v := uint32(f / physic.Hertz)
|
||||
return toErr("SetBaudRate", h.h.SetBaudRate(v))
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
func toErr(s string, e d2xx.Err) error {
|
||||
if e == 0 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("ftdi: " + s + ": " + e.String())
|
||||
}
|
295
vendor/periph.io/x/host/v3/ftdi/i2c.go
generated
vendored
Normal file
295
vendor/periph.io/x/host/v3/ftdi/i2c.go
generated
vendored
Normal file
@ -0,0 +1,295 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// This functionality requires MPSSE.
|
||||
//
|
||||
// Interfacing I²C:
|
||||
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_113_FTDI_Hi_Speed_USB_To_I2C_Example.pdf
|
||||
//
|
||||
// Implementation based on
|
||||
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_255_USB%20to%20I2C%20Example%20using%20the%20FT232H%20and%20FT201X%20devices.pdf
|
||||
//
|
||||
// Page 18: MPSSE does not automatically support clock stretching for I²C.
|
||||
|
||||
package ftdi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"periph.io/x/conn/v3"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/i2c"
|
||||
"periph.io/x/conn/v3/physic"
|
||||
)
|
||||
|
||||
const i2cSCL = 1 // D0
|
||||
const i2cSDAOut = 2 // D1
|
||||
const i2cSDAIn = 4 // D2
|
||||
|
||||
type i2cBus struct {
|
||||
f *FT232H
|
||||
pullUp bool
|
||||
}
|
||||
|
||||
// Close stops I²C mode, returns to high speed mode, disable tri-state.
|
||||
func (d *i2cBus) Close() error {
|
||||
d.f.mu.Lock()
|
||||
err := d.stopI2C()
|
||||
d.f.mu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// Duplex implements conn.Conn.
|
||||
func (d *i2cBus) Duplex() conn.Duplex {
|
||||
return conn.Half
|
||||
}
|
||||
|
||||
func (d *i2cBus) String() string {
|
||||
return d.f.String()
|
||||
}
|
||||
|
||||
// SetSpeed implements i2c.Bus.
|
||||
func (d *i2cBus) SetSpeed(f physic.Frequency) error {
|
||||
if f > 10*physic.MegaHertz {
|
||||
return fmt.Errorf("d2xx: invalid speed %s; maximum supported clock is 10MHz", f)
|
||||
}
|
||||
if f < 100*physic.Hertz {
|
||||
return fmt.Errorf("d2xx: invalid speed %s; minimum supported clock is 100Hz; did you forget to multiply by physic.KiloHertz?", f)
|
||||
}
|
||||
d.f.mu.Lock()
|
||||
defer d.f.mu.Unlock()
|
||||
_, err := d.f.h.MPSSEClock(f * 2 / 3)
|
||||
return err
|
||||
}
|
||||
|
||||
// Tx implements i2c.Bus.
|
||||
func (d *i2cBus) Tx(addr uint16, w, r []byte) error {
|
||||
d.f.mu.Lock()
|
||||
defer d.f.mu.Unlock()
|
||||
if err := d.setI2CStart(); err != nil {
|
||||
return err
|
||||
}
|
||||
a := [1]byte{byte(addr)}
|
||||
if err := d.writeBytes(a[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(w) != 0 {
|
||||
if err := d.writeBytes(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(r) != 0 {
|
||||
if err := d.readBytes(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := d.setI2CStop(); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.setI2CLinesIdle()
|
||||
}
|
||||
|
||||
// SCL implements i2c.Pins.
|
||||
func (d *i2cBus) SCL() gpio.PinIO {
|
||||
return d.f.D0
|
||||
}
|
||||
|
||||
// SDA implements i2c.Pins.
|
||||
func (d *i2cBus) SDA() gpio.PinIO {
|
||||
return d.f.D1
|
||||
}
|
||||
|
||||
// setupI2C initializes the MPSSE to the state to run an I²C transaction.
|
||||
//
|
||||
// Defaults to 400kHz.
|
||||
//
|
||||
// When pullUp is true; output alternates between Out(Low) and In(PullUp).
|
||||
//
|
||||
// when pullUp is false; pins are set in Tristate so Out(High) becomes float
|
||||
// instead of drive High. Low still drives low. That's called open collector.
|
||||
func (d *i2cBus) setupI2C(pullUp bool) error {
|
||||
if pullUp {
|
||||
return errors.New("d2xx: PullUp will soon be implemented")
|
||||
}
|
||||
// TODO(maruel): We could set these only *during* the I²C operation, which
|
||||
// would make more sense.
|
||||
f := 400 * physic.KiloHertz
|
||||
clk := ((30 * physic.MegaHertz / f) - 1) * 2 / 3
|
||||
|
||||
buf := [4 + 3]byte{
|
||||
clock3Phase,
|
||||
clock30MHz, byte(clk), byte(clk >> 8),
|
||||
}
|
||||
cmd := buf[:4]
|
||||
if !d.pullUp {
|
||||
// TODO(maruel): Do not mess with other GPIOs tristate.
|
||||
cmd = append(cmd, dataTristate, 7, 0)
|
||||
}
|
||||
if _, err := d.f.h.Write(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
d.f.usingI2C = true
|
||||
d.pullUp = pullUp
|
||||
return d.setI2CLinesIdle()
|
||||
}
|
||||
|
||||
// stopI2C resets the MPSSE to a more "normal" state.
|
||||
func (d *i2cBus) stopI2C() error {
|
||||
// Resets to 30MHz.
|
||||
buf := [4 + 3]byte{
|
||||
clock2Phase,
|
||||
clock30MHz, 0, 0,
|
||||
}
|
||||
cmd := buf[:4]
|
||||
if !d.pullUp {
|
||||
// TODO(maruel): Do not mess with other GPIOs tristate.
|
||||
cmd = append(cmd, dataTristate, 0, 0)
|
||||
}
|
||||
_, err := d.f.h.Write(cmd)
|
||||
d.f.usingI2C = false
|
||||
return err
|
||||
}
|
||||
|
||||
// setI2CLinesIdle sets all D0 and D1 lines high.
|
||||
//
|
||||
// Does not touch D3~D7.
|
||||
func (d *i2cBus) setI2CLinesIdle() error {
|
||||
const mask = 0xFF &^ (i2cSCL | i2cSDAOut | i2cSDAIn)
|
||||
// TODO(maruel): d.pullUp
|
||||
d.f.dbus.direction = d.f.dbus.direction&mask | i2cSCL | i2cSDAOut
|
||||
d.f.dbus.value = d.f.dbus.value & mask
|
||||
cmd := [...]byte{gpioSetD, d.f.dbus.value | i2cSCL | i2cSDAOut, d.f.dbus.direction}
|
||||
_, err := d.f.h.Write(cmd[:])
|
||||
return err
|
||||
}
|
||||
|
||||
// setI2CStart starts an I²C transaction.
|
||||
//
|
||||
// Does not touch D3~D7.
|
||||
func (d *i2cBus) setI2CStart() error {
|
||||
// TODO(maruel): d.pullUp
|
||||
dir := d.f.dbus.direction
|
||||
v := d.f.dbus.value
|
||||
// Assumes last setup was d.setI2CLinesIdle(), e.g. D0 and D1 are high, so
|
||||
// skip this.
|
||||
//
|
||||
// Runs the command 4 times as a way to delay execution.
|
||||
cmd := [...]byte{
|
||||
// SCL high, SDA low for 600ns
|
||||
gpioSetD, v | i2cSCL, dir,
|
||||
gpioSetD, v | i2cSCL, dir,
|
||||
gpioSetD, v | i2cSCL, dir,
|
||||
gpioSetD, v | i2cSCL, dir,
|
||||
// SCL low, SDA low
|
||||
gpioSetD, v, dir,
|
||||
gpioSetD, v, dir,
|
||||
gpioSetD, v, dir,
|
||||
}
|
||||
_, err := d.f.h.Write(cmd[:])
|
||||
return err
|
||||
}
|
||||
|
||||
// setI2CStop completes an I²C transaction.
|
||||
//
|
||||
// Does not touch D3~D7.
|
||||
func (d *i2cBus) setI2CStop() error {
|
||||
// TODO(maruel): d.pullUp
|
||||
dir := d.f.dbus.direction
|
||||
v := d.f.dbus.value
|
||||
// Runs the command 4 times as a way to delay execution.
|
||||
cmd := [...]byte{
|
||||
// SCL low, SDA low
|
||||
gpioSetD, v, dir,
|
||||
gpioSetD, v, dir,
|
||||
gpioSetD, v, dir,
|
||||
gpioSetD, v, dir,
|
||||
// SCL high, SDA low
|
||||
gpioSetD, v | i2cSCL, dir,
|
||||
gpioSetD, v | i2cSCL, dir,
|
||||
gpioSetD, v | i2cSCL, dir,
|
||||
gpioSetD, v | i2cSCL, dir,
|
||||
// SCL high, SDA high
|
||||
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
|
||||
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
|
||||
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
|
||||
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
|
||||
}
|
||||
_, err := d.f.h.Write(cmd[:])
|
||||
return err
|
||||
}
|
||||
|
||||
// writeBytes writes multiple bytes within an I²C transaction.
|
||||
//
|
||||
// Does not touch D3~D7.
|
||||
func (d *i2cBus) writeBytes(w []byte) error {
|
||||
// TODO(maruel): d.pullUp
|
||||
dir := d.f.dbus.direction
|
||||
v := d.f.dbus.value
|
||||
// TODO(maruel): WAT?
|
||||
if err := d.f.h.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(maruel): Implement both with and without NAK check.
|
||||
var r [1]byte
|
||||
cmd := [...]byte{
|
||||
// Data out, the 0 will be replaced with the byte.
|
||||
dataOut | dataOutFall, 0, 0, 0,
|
||||
// Set back to idle.
|
||||
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
|
||||
// Read ACK/NAK.
|
||||
dataIn | dataBit, 0,
|
||||
flush,
|
||||
}
|
||||
for _, c := range w {
|
||||
cmd[3] = c
|
||||
if _, err := d.f.h.Write(cmd[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := d.f.h.ReadAll(context.Background(), r[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if r[0]&1 == 0 {
|
||||
return errors.New("got NAK")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readBytes reads multiple bytes within an I²C transaction.
|
||||
//
|
||||
// Does not touch D3~D7.
|
||||
func (d *i2cBus) readBytes(r []byte) error {
|
||||
// TODO(maruel): d.pullUp
|
||||
dir := d.f.dbus.direction
|
||||
v := d.f.dbus.value
|
||||
|
||||
cmd := [...]byte{
|
||||
// Read 8 bits.
|
||||
dataIn | dataBit, 7,
|
||||
// Send ACK/NAK.
|
||||
dataOut | dataOutFall | dataBit, 0, 0,
|
||||
// Set back to idle.
|
||||
gpioSetD, v | i2cSCL | i2cSDAOut, dir,
|
||||
// Force read buffer flush. This is only necessary if NAK are not ignored.
|
||||
flush,
|
||||
}
|
||||
for i := range r {
|
||||
if i == len(r)-1 {
|
||||
// NAK.
|
||||
cmd[4] = 0x80
|
||||
}
|
||||
if _, err := d.f.h.Write(cmd[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := d.f.h.ReadAll(context.Background(), r[i:1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ i2c.BusCloser = &i2cBus{}
|
||||
var _ i2c.Pins = &i2cBus{}
|
452
vendor/periph.io/x/host/v3/ftdi/mpsse.go
generated
vendored
Normal file
452
vendor/periph.io/x/host/v3/ftdi/mpsse.go
generated
vendored
Normal file
@ -0,0 +1,452 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// MPSSE is Multi-Protocol Synchronous Serial Engine
|
||||
//
|
||||
// MPSSE basics:
|
||||
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_135_MPSSE_Basics.pdf
|
||||
//
|
||||
// MPSSE and MCU emulation modes:
|
||||
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf
|
||||
|
||||
package ftdi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/physic"
|
||||
)
|
||||
|
||||
const (
|
||||
// TDI/TDO serial operation synchronised on clock edges.
|
||||
//
|
||||
// Long streams (default):
|
||||
// - [1, 65536] bytes (length is sent minus one, requires 8 bits multiple)
|
||||
// <op>, <LengthLow-1>, <LengthHigh-1>, <byte0>, ..., <byteN>
|
||||
//
|
||||
// Short streams (dataBit is specified):
|
||||
// - [1, 8] bits
|
||||
// <op>, <Length-1>, <byte>
|
||||
//
|
||||
// When both dataOut and dataIn are specified, one of dataOutFall or
|
||||
// dataInFall should be specified, at least for most sane protocols.
|
||||
//
|
||||
// Flags:
|
||||
dataOut byte = 0x10 // Enable output, default on +VE (Rise)
|
||||
dataIn byte = 0x20 // Enable input, default on +VE (Rise)
|
||||
dataOutFall byte = 0x01 // instead of Rise
|
||||
dataInFall byte = 0x04 // instead of Rise
|
||||
dataLSBF byte = 0x08 // instead of MSBF
|
||||
dataBit byte = 0x02 // instead of Byte
|
||||
|
||||
// Data line drives low when the data is 0 and tristates on data 1. This is
|
||||
// used with I²C.
|
||||
// <op>, <ADBus pins>, <ACBus pins>
|
||||
dataTristate byte = 0x9E
|
||||
|
||||
// TSM operation (for JTAG).
|
||||
//
|
||||
// - Send bits 6 to 0 to the TMS pin using LSB or MSB.
|
||||
// - Bit 7 is passed to TDI/DO before the first clock of TMS and is held
|
||||
// static for the duration of TMS clocking.
|
||||
//
|
||||
// <op>, <Length>, <byte>
|
||||
tmsOutLSBFRise byte = 0x4A
|
||||
tmsOutLSBFFall byte = 0x4B
|
||||
tmsIOLSBInRise byte = 0x6A
|
||||
tmsIOLSBInFall byte = 0x6B
|
||||
// Unclear: 0x6E and 0x6F
|
||||
|
||||
// GPIO operation.
|
||||
//
|
||||
// - Operates on 8 GPIOs at a time, e.g. C0~C7 or D0~D7.
|
||||
// - Direction 1 means output, 0 means input.
|
||||
//
|
||||
// <op>, <value>, <direction>
|
||||
gpioSetD byte = 0x80
|
||||
gpioSetC byte = 0x82
|
||||
// <op>, returns <value>
|
||||
gpioReadD byte = 0x81
|
||||
gpioReadC byte = 0x83
|
||||
|
||||
// Internal loopback.
|
||||
//
|
||||
// Connects TDI and TDO together.
|
||||
internalLoopbackEnable byte = 0x84
|
||||
internalLoopbackDisable byte = 0x85
|
||||
|
||||
// Clock.
|
||||
//
|
||||
// The TCK/SK has a 50% duty cycle.
|
||||
//
|
||||
// The inactive clock state can be set via the gpioSetD command and control
|
||||
// bit 0.
|
||||
//
|
||||
// By default, the base clock is 6MHz via a 5x divisor. On
|
||||
// FT232H/FT2232H/FT4232H, the 5x divisor can be disabled.
|
||||
clock30MHz byte = 0x8A
|
||||
clock6MHz byte = 0x8B
|
||||
// Sets clock divisor.
|
||||
//
|
||||
// The effective value depends if clock30MHz was sent or not.
|
||||
//
|
||||
// - 0(1) 6MHz / 30MHz
|
||||
// - 1(2) 3MHz / 15MHz
|
||||
// - 2(3) 2MHz / 10MHz
|
||||
// - 3(4) 1.5MHz / 7.5MHz
|
||||
// - 4(5) 1.25MHz / 6MHz
|
||||
// - ...
|
||||
// - 0xFFFF(65536) 91.553Hz / 457.763Hz
|
||||
//
|
||||
// <op>, <valueL-1>, <valueH-1>
|
||||
clockSetDivisor byte = 0x86
|
||||
// Uses 3 phases data clocking: data is valid on both clock edges. Needed
|
||||
// for I²C.
|
||||
clock3Phase byte = 0x8C
|
||||
// Uses normal 2 phases data clocking.
|
||||
clock2Phase byte = 0x8D
|
||||
// Enables clock even while not doing any operation. Used with JTAG.
|
||||
// Enables the clock between [1, 8] pulses.
|
||||
// <op>, <length-1>
|
||||
clockOnShort byte = 0x8E
|
||||
// Enables the clock between [8, 524288] pulses in 8 multiples.
|
||||
// <op>, <lengthL-1>, <lengthH-1>
|
||||
clockOnLong byte = 0x8F
|
||||
// Enables clock until D5 is high or low. Used with JTAG.
|
||||
clockUntilHigh byte = 0x94
|
||||
clockUntilLow byte = 0x95
|
||||
// <op>, <lengthL-1>, <lengthH-1> in 8 multiples.
|
||||
clockUntilHighLong byte = 0x9C
|
||||
clockUntilLowLong byte = 0x9D
|
||||
// Enables adaptive clocking. Used with JTAG.
|
||||
//
|
||||
// This causes the controller to wait for D7 signal state as an ACK.
|
||||
clockAdaptive byte = 0x96
|
||||
// Disables adaptive clocking.
|
||||
clockNormal byte = 0x97
|
||||
|
||||
// CPU mode.
|
||||
//
|
||||
// Access the device registers like a memory mapped device.
|
||||
//
|
||||
// <op>, <addrLow>
|
||||
cpuReadShort byte = 0x90
|
||||
// <op>, <addrHi>, <addrLow>
|
||||
cpuReadFar byte = 0x91
|
||||
// <op>, <addrLow>, <data>
|
||||
cpuWriteShort byte = 0x92
|
||||
// <op>, <addrHi>, <addrLow>, <data>
|
||||
cpuWriteFar byte = 0x91
|
||||
|
||||
// Buffer operations.
|
||||
//
|
||||
// Flush the buffer back to the host.
|
||||
flush byte = 0x87
|
||||
// Wait until D5 (JTAG) or I/O1 (CPU) is high. Once it is detected as
|
||||
// high, the MPSSE engine moves on to process the next instruction.
|
||||
waitHigh byte = 0x88
|
||||
waitLow byte = 0x89
|
||||
)
|
||||
|
||||
// InitMPSSE sets the device into MPSSE mode.
|
||||
//
|
||||
// This requires a f232h, ft2232, ft2232h or a ft4232h.
|
||||
//
|
||||
// Use only one of Init or InitMPSSE.
|
||||
func (h *handle) InitMPSSE() error {
|
||||
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_255_USB%20to%20I2C%20Example%20using%20the%20FT232H%20and%20FT201X%20devices.pdf
|
||||
// Pre-state:
|
||||
// - Write EEPROM i.IsFifo = true so the device DBus is started in tristate.
|
||||
|
||||
// Try to verify the MPSSE controller without initializing it first. This is
|
||||
// the 'happy path', which enables reusing the device is its current state
|
||||
// without affecting current GPIO state.
|
||||
if h.mpsseVerify() != nil {
|
||||
// Do a full reset. Just trying to set the MPSSE controller will
|
||||
// likely not work. That's a layering violation (since the retry with reset
|
||||
// is done in driver.go) but we've survived worse things...
|
||||
//
|
||||
// TODO(maruel): This is not helping in practice, this need to be fine
|
||||
// tuned.
|
||||
if err := h.Reset(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := h.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := h.SetBitMode(0, bitModeMpsse); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := h.mpsseVerify(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize MPSSE to a known state.
|
||||
// Reset the clock since it is impossible to read back the current clock rate.
|
||||
// Reset all the GPIOs are inputs since it is impossible to read back the
|
||||
// state of each GPIO (if they are input or output).
|
||||
cmd := []byte{
|
||||
clock30MHz, clockNormal, clock2Phase, internalLoopbackDisable,
|
||||
gpioSetC, 0x00, 0x00,
|
||||
gpioSetD, 0x00, 0x00,
|
||||
}
|
||||
if _, err := h.Write(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
// Success!!
|
||||
return nil
|
||||
}
|
||||
|
||||
// mpsseVerify sends an invalid MPSSE command and verifies the returned value
|
||||
// is incorrect.
|
||||
//
|
||||
// In practice this takes around 2ms.
|
||||
func (h *handle) mpsseVerify() error {
|
||||
var b [16]byte
|
||||
for _, v := range []byte{0xAA, 0xAB} {
|
||||
// Write a bad command and ensure it returned correctly.
|
||||
// Unlike what the application note proposes, include a flush op right
|
||||
// after. Without the flush, the device will only flush after the delay
|
||||
// specified to SetLatencyTimer. The flush removes this unneeded wait,
|
||||
// which enables increasing the delay specified to SetLatencyTimer.
|
||||
b[0] = v
|
||||
b[1] = flush
|
||||
if _, err := h.Write(b[:2]); err != nil {
|
||||
return fmt.Errorf("d2xx: mpsseVerify: %v", err)
|
||||
}
|
||||
// Sometimes, especially right after a reset, the device spews a few bytes.
|
||||
// Discard them. This significantly increases the odds of a successful
|
||||
// initialization.
|
||||
p, e := h.h.GetQueueStatus()
|
||||
if e != 0 {
|
||||
return toErr("Read/GetQueueStatus", e)
|
||||
}
|
||||
for p > 2 {
|
||||
l := int(p) - 2
|
||||
if l > len(b) {
|
||||
l = len(b)
|
||||
}
|
||||
// Discard the overflow bytes.
|
||||
ctx, cancel := context200ms()
|
||||
defer cancel()
|
||||
if _, err := h.ReadAll(ctx, b[:l]); err != nil {
|
||||
return fmt.Errorf("d2xx: mpsseVerify: %v", err)
|
||||
}
|
||||
p -= uint32(l)
|
||||
}
|
||||
// Custom implementation, as we want to flush any stray byte.
|
||||
ctx, cancel := context200ms()
|
||||
defer cancel()
|
||||
if _, err := h.ReadAll(ctx, b[:2]); err != nil {
|
||||
return fmt.Errorf("d2xx: mpsseVerify: %v", err)
|
||||
}
|
||||
// 0xFA means invalid command, 0xAA is the command echoed back.
|
||||
if b[0] != 0xFA || b[1] != v {
|
||||
return fmt.Errorf("d2xx: mpsseVerify: failed test for byte %#x: %#x", v, b)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// MPSSERegRead reads the memory mapped registers from the device.
|
||||
func (h *handle) MPSSERegRead(addr uint16) (byte, error) {
|
||||
// Unlike most other operations, the uint16 byte order is <hi>, <lo>.
|
||||
b := [...]byte{cpuReadFar, byte(addr >> 8), byte(addr), flush}
|
||||
if _, err := h.Write(b[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ctx, cancel := context200ms()
|
||||
defer cancel()
|
||||
_, err := h.ReadAll(ctx, b[:1])
|
||||
return b[0], err
|
||||
}
|
||||
|
||||
// MPSSEClock sets the clock at the closest value and returns it.
|
||||
func (h *handle) MPSSEClock(f physic.Frequency) (physic.Frequency, error) {
|
||||
// TODO(maruel): Memory clock and skip if the same value.
|
||||
clk := clock30MHz
|
||||
base := 30 * physic.MegaHertz
|
||||
div := base / f
|
||||
if div >= 65536 {
|
||||
clk = clock6MHz
|
||||
base /= 5
|
||||
div = base / f
|
||||
if div >= 65536 {
|
||||
return 0, errors.New("d2xx: clock frequency is too low")
|
||||
}
|
||||
}
|
||||
b := [...]byte{clk, clockSetDivisor, byte(div - 1), byte((div - 1) >> 8)}
|
||||
_, err := h.Write(b[:])
|
||||
return base / div, err
|
||||
}
|
||||
|
||||
// mpsseTxOp returns the right MPSSE command byte for the stream.
|
||||
func mpsseTxOp(w, r bool, ew, er gpio.Edge, lsbf bool) byte {
|
||||
op := byte(0)
|
||||
if lsbf {
|
||||
op |= dataLSBF
|
||||
}
|
||||
if w {
|
||||
op |= dataOut
|
||||
if ew == gpio.FallingEdge {
|
||||
op |= dataOutFall
|
||||
}
|
||||
}
|
||||
if r {
|
||||
op |= dataIn
|
||||
if er == gpio.FallingEdge {
|
||||
op |= dataInFall
|
||||
}
|
||||
}
|
||||
return op
|
||||
}
|
||||
|
||||
// MPSSETx runs a transaction on the clock on pins D0, D1 and D2.
|
||||
//
|
||||
// It can only do it on a multiple of 8 bits.
|
||||
func (h *handle) MPSSETx(w, r []byte, ew, er gpio.Edge, lsbf bool) error {
|
||||
l := len(w)
|
||||
if len(w) != 0 {
|
||||
// TODO(maruel): This is easy to fix by daisy chaining operations.
|
||||
if len(w) > 65536 {
|
||||
return errors.New("d2xx: write buffer too long; max 65536")
|
||||
}
|
||||
}
|
||||
if len(r) != 0 {
|
||||
if len(r) > 65536 {
|
||||
return errors.New("d2xx: read buffer too long; max 65536")
|
||||
}
|
||||
if l != 0 && len(r) != l {
|
||||
return errors.New("d2xx: mismatched buffer lengths")
|
||||
}
|
||||
l = len(r)
|
||||
}
|
||||
// The FT232H has 1Kb Tx and Rx buffers. So partial writes should be done.
|
||||
// TODO(maruel): Test.
|
||||
|
||||
// Flush can be useful if rbits != 0.
|
||||
op := mpsseTxOp(len(w) != 0, len(r) != 0, ew, er, lsbf)
|
||||
cmd := []byte{op, byte(l - 1), byte((l - 1) >> 8)}
|
||||
cmd = append(cmd, w...)
|
||||
if len(r) != 0 {
|
||||
cmd = append(cmd, flush)
|
||||
}
|
||||
if _, err := h.Write(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(r) != 0 {
|
||||
ctx, cancel := context200ms()
|
||||
defer cancel()
|
||||
_, err := h.ReadAll(ctx, r)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MPSSETxShort runs a transaction on the clock pins D0, D1 and D2 for a byte
|
||||
// or less: between 1 and 8 bits.
|
||||
func (h *handle) MPSSETxShort(w byte, wbits, rbits int, ew, er gpio.Edge, lsbf bool) (byte, error) {
|
||||
op := byte(dataBit)
|
||||
if lsbf {
|
||||
op |= dataLSBF
|
||||
}
|
||||
l := wbits
|
||||
if wbits != 0 {
|
||||
if wbits > 8 {
|
||||
return 0, errors.New("d2xx: write buffer too long; max 8")
|
||||
}
|
||||
op |= dataOut
|
||||
if ew == gpio.FallingEdge {
|
||||
op |= dataOutFall
|
||||
}
|
||||
}
|
||||
if rbits != 0 {
|
||||
if rbits > 8 {
|
||||
return 0, errors.New("d2xx: read buffer too long; max 8")
|
||||
}
|
||||
op |= dataIn
|
||||
if er == gpio.FallingEdge {
|
||||
op |= dataInFall
|
||||
}
|
||||
if l != 0 && rbits != l {
|
||||
return 0, errors.New("d2xx: mismatched buffer lengths")
|
||||
}
|
||||
l = rbits
|
||||
}
|
||||
b := [3]byte{op, byte(l - 1)}
|
||||
cmd := b[:2]
|
||||
if wbits != 0 {
|
||||
cmd = append(cmd, w)
|
||||
}
|
||||
if rbits != 0 {
|
||||
cmd = append(cmd, flush)
|
||||
}
|
||||
if _, err := h.Write(cmd); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if rbits != 0 {
|
||||
ctx, cancel := context200ms()
|
||||
defer cancel()
|
||||
_, err := h.ReadAll(ctx, b[:1])
|
||||
return b[0], err
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// MPSSECBus operates on 8 GPIOs at a time C0~C7.
|
||||
//
|
||||
// Direction 1 means output, 0 means input.
|
||||
func (h *handle) MPSSECBus(mask, value byte) error {
|
||||
b := [...]byte{gpioSetC, value, mask}
|
||||
_, err := h.Write(b[:])
|
||||
return err
|
||||
}
|
||||
|
||||
// MPSSEDBus operates on 8 GPIOs at a time D0~D7.
|
||||
//
|
||||
// Direction 1 means output, 0 means input.
|
||||
func (h *handle) MPSSEDBus(mask, value byte) error {
|
||||
b := [...]byte{gpioSetD, value, mask}
|
||||
_, err := h.Write(b[:])
|
||||
return err
|
||||
}
|
||||
|
||||
// MPSSECBusRead reads all the CBus pins C0~C7.
|
||||
func (h *handle) MPSSECBusRead() (byte, error) {
|
||||
b := [...]byte{gpioReadC, flush}
|
||||
if _, err := h.Write(b[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ctx, cancel := context200ms()
|
||||
defer cancel()
|
||||
if _, err := h.ReadAll(ctx, b[:1]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return b[0], nil
|
||||
}
|
||||
|
||||
// MPSSEDBusRead reads all the DBus pins D0~D7.
|
||||
func (h *handle) MPSSEDBusRead() (byte, error) {
|
||||
b := [...]byte{gpioReadD, flush}
|
||||
if _, err := h.Write(b[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ctx, cancel := context200ms()
|
||||
defer cancel()
|
||||
if _, err := h.ReadAll(ctx, b[:1]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return b[0], nil
|
||||
}
|
||||
|
||||
func context200ms() (context.Context, func()) {
|
||||
return context.WithTimeout(context.Background(), 200*time.Millisecond)
|
||||
}
|
205
vendor/periph.io/x/host/v3/ftdi/mpsse_gpio.go
generated
vendored
Normal file
205
vendor/periph.io/x/host/v3/ftdi/mpsse_gpio.go
generated
vendored
Normal file
@ -0,0 +1,205 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package ftdi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/physic"
|
||||
)
|
||||
|
||||
// gpiosMPSSE is a slice of 8 GPIO pins driven via MPSSE.
|
||||
//
|
||||
// This permits keeping a cache.
|
||||
type gpiosMPSSE struct {
|
||||
// Immutable.
|
||||
h *handle
|
||||
cbus bool // false if D bus
|
||||
pins [8]gpioMPSSE
|
||||
|
||||
// Cache of values
|
||||
direction byte
|
||||
value byte
|
||||
}
|
||||
|
||||
func (g *gpiosMPSSE) init(name string) {
|
||||
s := "D"
|
||||
if g.cbus {
|
||||
s = "C"
|
||||
}
|
||||
// Configure pulls; pull ups are 75kΩ.
|
||||
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_184%20FTDI%20Device%20Input%20Output%20Pin%20States.pdf
|
||||
// has a good table.
|
||||
// D0, D2 and D4 go in high impedance before going into pull up.
|
||||
// TODO(maruel): The pull on CBus depends on EEPROM!
|
||||
for i := range g.pins {
|
||||
g.pins[i].a = g
|
||||
g.pins[i].n = name + "." + s + strconv.Itoa(i)
|
||||
g.pins[i].num = i
|
||||
g.pins[i].dp = gpio.PullUp
|
||||
}
|
||||
if g.cbus {
|
||||
// That's just the default EEPROM value.
|
||||
g.pins[7].dp = gpio.PullDown
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gpiosMPSSE) in(n int) error {
|
||||
if g.h == nil {
|
||||
return errors.New("d2xx: device not open")
|
||||
}
|
||||
g.direction = g.direction & ^(1 << uint(n))
|
||||
if g.cbus {
|
||||
return g.h.MPSSECBus(g.direction, g.value)
|
||||
}
|
||||
return g.h.MPSSEDBus(g.direction, g.value)
|
||||
}
|
||||
|
||||
func (g *gpiosMPSSE) read() (byte, error) {
|
||||
if g.h == nil {
|
||||
return 0, errors.New("d2xx: device not open")
|
||||
}
|
||||
var err error
|
||||
if g.cbus {
|
||||
g.value, err = g.h.MPSSECBusRead()
|
||||
} else {
|
||||
g.value, err = g.h.MPSSEDBusRead()
|
||||
}
|
||||
return g.value, err
|
||||
}
|
||||
|
||||
func (g *gpiosMPSSE) out(n int, l gpio.Level) error {
|
||||
if g.h == nil {
|
||||
return errors.New("d2xx: device not open")
|
||||
}
|
||||
g.direction = g.direction | (1 << uint(n))
|
||||
if l {
|
||||
g.value |= 1 << uint(n)
|
||||
} else {
|
||||
g.value &^= 1 << uint(n)
|
||||
}
|
||||
if g.cbus {
|
||||
return g.h.MPSSECBus(g.direction, g.value)
|
||||
}
|
||||
return g.h.MPSSEDBus(g.direction, g.value)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// gpioMPSSE is a GPIO pin on a FTDI device driven via MPSSE.
|
||||
//
|
||||
// gpioMPSSE implements gpio.PinIO.
|
||||
//
|
||||
// It is immutable and stateless.
|
||||
type gpioMPSSE struct {
|
||||
a *gpiosMPSSE
|
||||
n string
|
||||
num int
|
||||
dp gpio.Pull
|
||||
}
|
||||
|
||||
// String implements pin.Pin.
|
||||
func (g *gpioMPSSE) String() string {
|
||||
return g.n
|
||||
}
|
||||
|
||||
// Name implements pin.Pin.
|
||||
func (g *gpioMPSSE) Name() string {
|
||||
return g.n
|
||||
}
|
||||
|
||||
// Number implements pin.Pin.
|
||||
func (g *gpioMPSSE) Number() int {
|
||||
return g.num
|
||||
}
|
||||
|
||||
// Function implements pin.Pin.
|
||||
func (g *gpioMPSSE) Function() string {
|
||||
s := "Out/"
|
||||
m := byte(1 << uint(g.num))
|
||||
if g.a.direction&m == 0 {
|
||||
s = "In/"
|
||||
_, _ = g.a.read()
|
||||
}
|
||||
return s + gpio.Level(g.a.value&m != 0).String()
|
||||
}
|
||||
|
||||
// Halt implements gpio.PinIO.
|
||||
func (g *gpioMPSSE) Halt() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// In implements gpio.PinIn.
|
||||
func (g *gpioMPSSE) In(pull gpio.Pull, e gpio.Edge) error {
|
||||
if e != gpio.NoEdge {
|
||||
// We could support it on D5.
|
||||
return errors.New("d2xx: edge triggering is not supported")
|
||||
}
|
||||
if pull != g.dp && pull != gpio.PullNoChange {
|
||||
// TODO(maruel): This needs to be redone:
|
||||
// - EEPROM values FT232hCBusTristatePullUp and FT232hCBusPwrEnable can be
|
||||
// used to control individual CBus pins.
|
||||
// - dataTristate enables gpio.Float when set to output High, but I don't
|
||||
// know if it will enable reading the value (?). This needs to be
|
||||
// confirmed.
|
||||
return fmt.Errorf("d2xx: pull %s is not supported; try %s", pull, g.dp)
|
||||
}
|
||||
return g.a.in(g.num)
|
||||
}
|
||||
|
||||
// Read implements gpio.PinIn.
|
||||
func (g *gpioMPSSE) Read() gpio.Level {
|
||||
v, _ := g.a.read()
|
||||
return gpio.Level(v&(1<<uint(g.num)) != 0)
|
||||
}
|
||||
|
||||
// WaitForEdge implements gpio.PinIn.
|
||||
func (g *gpioMPSSE) WaitForEdge(t time.Duration) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultPull implements gpio.PinIn.
|
||||
func (g *gpioMPSSE) DefaultPull() gpio.Pull {
|
||||
return g.dp
|
||||
}
|
||||
|
||||
// Pull implements gpio.PinIn. The resistor is 75kΩ.
|
||||
func (g *gpioMPSSE) Pull() gpio.Pull {
|
||||
// See In() for the challenges.
|
||||
return g.dp
|
||||
}
|
||||
|
||||
// Out implements gpio.PinOut.
|
||||
func (g *gpioMPSSE) Out(l gpio.Level) error {
|
||||
return g.a.out(g.num, l)
|
||||
}
|
||||
|
||||
// PWM implements gpio.PinOut.
|
||||
func (g *gpioMPSSE) PWM(d gpio.Duty, f physic.Frequency) error {
|
||||
return errors.New("d2xx: not implemented")
|
||||
}
|
||||
|
||||
/*
|
||||
func (g *gpioMPSSE) Drive() physic.ElectricCurrent {
|
||||
//return g.a.ee.CDriveCurrent * physic.MilliAmpere
|
||||
return 2 * physic.MilliAmpere
|
||||
}
|
||||
|
||||
func (g *gpioMPSSE) SlewLimit() bool {
|
||||
//return g.a.ee.CSlowSlew
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *gpioMPSSE) Hysteresis() bool {
|
||||
//return g.a.ee.DSchmittInput
|
||||
return true
|
||||
}
|
||||
*/
|
||||
|
||||
var _ gpio.PinIO = &gpioMPSSE{}
|
85
vendor/periph.io/x/host/v3/ftdi/pin.go
generated
vendored
Normal file
85
vendor/periph.io/x/host/v3/ftdi/pin.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Emulate independent GPIOs.
|
||||
|
||||
package ftdi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/physic"
|
||||
)
|
||||
|
||||
// invalidPin is a non-working (not implemented) pin on a FTDI device.
|
||||
//
|
||||
// invalidPin implements gpio.PinIO.
|
||||
type invalidPin struct {
|
||||
n string
|
||||
num int
|
||||
}
|
||||
|
||||
// String implements pin.Pin.
|
||||
func (p *invalidPin) String() string {
|
||||
return p.n
|
||||
}
|
||||
|
||||
// Name implements pin.Pin.
|
||||
func (p *invalidPin) Name() string {
|
||||
return p.n
|
||||
}
|
||||
|
||||
// Number implements pin.Pin.
|
||||
func (p *invalidPin) Number() int {
|
||||
return p.num
|
||||
}
|
||||
|
||||
// Function implements pin.Pin.
|
||||
func (p *invalidPin) Function() string {
|
||||
return "N/A"
|
||||
}
|
||||
|
||||
// Halt implements gpio.PinIO.
|
||||
func (p *invalidPin) Halt() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// In implements gpio.PinIn.
|
||||
func (p *invalidPin) In(pull gpio.Pull, e gpio.Edge) error {
|
||||
return errors.New("d2xx: to be implemented")
|
||||
}
|
||||
|
||||
// Read implements gpio.PinIn.
|
||||
func (p *invalidPin) Read() gpio.Level {
|
||||
return gpio.Low
|
||||
}
|
||||
|
||||
// WaitForEdge implements gpio.PinIn.
|
||||
func (p *invalidPin) WaitForEdge(t time.Duration) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Pull implements gpio.PinIn.
|
||||
func (p *invalidPin) Pull() gpio.Pull {
|
||||
return gpio.PullNoChange
|
||||
}
|
||||
|
||||
// DefaultPull implements gpio.PinIn.
|
||||
func (p *invalidPin) DefaultPull() gpio.Pull {
|
||||
return gpio.PullNoChange
|
||||
}
|
||||
|
||||
// Out implements gpio.PinOut.
|
||||
func (p *invalidPin) Out(l gpio.Level) error {
|
||||
return errors.New("d2xx: to be implemented")
|
||||
}
|
||||
|
||||
// PWM implements gpio.PinOut.
|
||||
func (p *invalidPin) PWM(d gpio.Duty, f physic.Frequency) error {
|
||||
return errors.New("d2xx: to be implemented")
|
||||
}
|
||||
|
||||
var _ gpio.PinIO = &invalidPin{}
|
697
vendor/periph.io/x/host/v3/ftdi/spi.go
generated
vendored
Normal file
697
vendor/periph.io/x/host/v3/ftdi/spi.go
generated
vendored
Normal file
@ -0,0 +1,697 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// This functionality requires MPSSE.
|
||||
//
|
||||
// Interfacing SPI:
|
||||
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_114_FTDI_Hi_Speed_USB_To_SPI_Example.pdf
|
||||
//
|
||||
// Implementation based on
|
||||
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_180_FT232H%20MPSSE%20Example%20-%20USB%20Current%20Meter%20using%20the%20SPI%20interface.pdf
|
||||
|
||||
package ftdi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"periph.io/x/conn/v3"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/physic"
|
||||
"periph.io/x/conn/v3/spi"
|
||||
)
|
||||
|
||||
// spiMPSEEPort is an SPI port over a FTDI device in MPSSE mode using the data
|
||||
// command on the AD bus.
|
||||
type spiMPSEEPort struct {
|
||||
c spiMPSEEConn
|
||||
|
||||
// Mutable.
|
||||
maxFreq physic.Frequency
|
||||
}
|
||||
|
||||
func (s *spiMPSEEPort) Close() error {
|
||||
s.c.f.mu.Lock()
|
||||
s.c.f.usingSPI = false
|
||||
s.maxFreq = 0
|
||||
s.c.edgeInvert = false
|
||||
s.c.clkActiveLow = false
|
||||
s.c.noCS = false
|
||||
s.c.lsbFirst = false
|
||||
s.c.halfDuplex = false
|
||||
s.c.f.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *spiMPSEEPort) String() string {
|
||||
return s.c.f.String()
|
||||
}
|
||||
|
||||
// Connect implements spi.Port.
|
||||
func (s *spiMPSEEPort) Connect(f physic.Frequency, m spi.Mode, bits int) (spi.Conn, error) {
|
||||
if f > physic.GigaHertz {
|
||||
return nil, fmt.Errorf("d2xx: invalid speed %s; maximum supported clock is 30MHz", f)
|
||||
}
|
||||
if f > 30*physic.MegaHertz {
|
||||
// TODO(maruel): Figure out a way to communicate that the speed was lowered.
|
||||
// https://github.com/google/periph/issues/255
|
||||
f = 30 * physic.MegaHertz
|
||||
}
|
||||
if f < 100*physic.Hertz {
|
||||
return nil, fmt.Errorf("d2xx: invalid speed %s; minimum supported clock is 100Hz; did you forget to multiply by physic.MegaHertz?", f)
|
||||
}
|
||||
if bits&7 != 0 {
|
||||
return nil, errors.New("d2xx: bits must be multiple of 8")
|
||||
}
|
||||
if bits != 8 {
|
||||
return nil, errors.New("d2xx: implement bits per word above 8")
|
||||
}
|
||||
|
||||
s.c.f.mu.Lock()
|
||||
defer s.c.f.mu.Unlock()
|
||||
s.c.noCS = m&spi.NoCS != 0
|
||||
s.c.halfDuplex = m&spi.HalfDuplex != 0
|
||||
s.c.lsbFirst = m&spi.LSBFirst != 0
|
||||
m &^= spi.NoCS | spi.HalfDuplex | spi.LSBFirst
|
||||
if s.c.halfDuplex {
|
||||
return nil, errors.New("d2xx: spi.HalfDuplex is not yet supported (implementing wouldn't be too hard, please submit a PR")
|
||||
}
|
||||
if m < 0 || m > 3 {
|
||||
return nil, errors.New("d2xx: unknown spi mode")
|
||||
}
|
||||
s.c.edgeInvert = m&1 != 0
|
||||
s.c.clkActiveLow = m&2 != 0
|
||||
if s.maxFreq == 0 || f < s.maxFreq {
|
||||
// TODO(maruel): We could set these only *during* the SPI operation, which
|
||||
// would make more sense.
|
||||
if _, err := s.c.f.h.MPSSEClock(f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.maxFreq = f
|
||||
}
|
||||
s.c.resetIdle()
|
||||
if err := s.c.f.h.MPSSEDBus(s.c.f.dbus.direction, s.c.f.dbus.value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.c.f.usingSPI = true
|
||||
return &s.c, nil
|
||||
}
|
||||
|
||||
// LimitSpeed implements spi.Port.
|
||||
func (s *spiMPSEEPort) LimitSpeed(f physic.Frequency) error {
|
||||
if f > physic.GigaHertz {
|
||||
return fmt.Errorf("d2xx: invalid speed %s; maximum supported clock is 30MHz", f)
|
||||
}
|
||||
if f > 30*physic.MegaHertz {
|
||||
f = 30 * physic.MegaHertz
|
||||
}
|
||||
if f < 100*physic.Hertz {
|
||||
return errors.New("d2xx: minimum supported clock is 100Hz; did you forget to multiply by physic.MegaHertz?")
|
||||
}
|
||||
s.c.f.mu.Lock()
|
||||
defer s.c.f.mu.Unlock()
|
||||
if s.maxFreq != 0 && s.maxFreq <= f {
|
||||
return nil
|
||||
}
|
||||
s.maxFreq = f
|
||||
// TODO(maruel): We could set these only *during* the SPI operation, which
|
||||
// would make more sense.
|
||||
_, err := s.c.f.h.MPSSEClock(s.maxFreq)
|
||||
return err
|
||||
}
|
||||
|
||||
// CLK returns the SCK (clock) pin.
|
||||
func (s *spiMPSEEPort) CLK() gpio.PinOut {
|
||||
return s.c.CLK()
|
||||
}
|
||||
|
||||
// MOSI returns the SDO (master out, slave in) pin.
|
||||
func (s *spiMPSEEPort) MOSI() gpio.PinOut {
|
||||
return s.c.MOSI()
|
||||
}
|
||||
|
||||
// MISO returns the SDI (master in, slave out) pin.
|
||||
func (s *spiMPSEEPort) MISO() gpio.PinIn {
|
||||
return s.c.MISO()
|
||||
}
|
||||
|
||||
// CS returns the CSN (chip select) pin.
|
||||
func (s *spiMPSEEPort) CS() gpio.PinOut {
|
||||
return s.c.CS()
|
||||
}
|
||||
|
||||
type spiMPSEEConn struct {
|
||||
// Immutable.
|
||||
f *FT232H
|
||||
|
||||
// Initialized at Connect().
|
||||
edgeInvert bool // CPHA=1
|
||||
clkActiveLow bool // CPOL=1
|
||||
noCS bool // CS line is not changed
|
||||
lsbFirst bool // Default is MSB first
|
||||
halfDuplex bool // 3 wire mode
|
||||
}
|
||||
|
||||
func (s *spiMPSEEConn) String() string {
|
||||
return s.f.String()
|
||||
}
|
||||
|
||||
func (s *spiMPSEEConn) Tx(w, r []byte) error {
|
||||
var p = [1]spi.Packet{{W: w, R: r}}
|
||||
return s.TxPackets(p[:])
|
||||
}
|
||||
|
||||
func (s *spiMPSEEConn) Duplex() conn.Duplex {
|
||||
// TODO(maruel): Support half if there's a need.
|
||||
return conn.Full
|
||||
}
|
||||
|
||||
func (s *spiMPSEEConn) TxPackets(pkts []spi.Packet) error {
|
||||
// Verification.
|
||||
for _, p := range pkts {
|
||||
if p.KeepCS {
|
||||
return errors.New("d2xx: implement spi.Packet.KeepCS")
|
||||
}
|
||||
if p.BitsPerWord&7 != 0 {
|
||||
return errors.New("d2xx: bits must be a multiple of 8")
|
||||
}
|
||||
if p.BitsPerWord != 0 && p.BitsPerWord != 8 {
|
||||
return errors.New("d2xx: implement spi.Packet.BitsPerWord")
|
||||
}
|
||||
if err := verifyBuffers(p.W, p.R); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.f.mu.Lock()
|
||||
defer s.f.mu.Unlock()
|
||||
const clk = byte(1) << 0
|
||||
const mosi = byte(1) << 1
|
||||
const miso = byte(1) << 2
|
||||
const cs = byte(1) << 3
|
||||
s.resetIdle()
|
||||
idle := s.f.dbus.value
|
||||
start1 := idle
|
||||
if !s.noCS {
|
||||
start1 &^= cs
|
||||
}
|
||||
// In mode 0 and 2, start2 is not needed.
|
||||
start2 := start1
|
||||
stop := idle
|
||||
if s.edgeInvert {
|
||||
// This is needed to 'prime' the clock.
|
||||
start2 ^= clk
|
||||
// With mode 1 and 3, keep the clock steady while CS is being deasserted to
|
||||
// not create a spurious clock.
|
||||
stop ^= clk
|
||||
}
|
||||
ew := gpio.FallingEdge
|
||||
er := gpio.RisingEdge
|
||||
if s.edgeInvert {
|
||||
ew, er = er, ew
|
||||
}
|
||||
if s.clkActiveLow {
|
||||
// TODO(maruel): Not sure.
|
||||
ew, er = er, ew
|
||||
}
|
||||
|
||||
// FT232H claims 512 USB packet support, so to reduce the chatter over USB,
|
||||
// try to make all I/O be aligned on this amount. This also removes the need
|
||||
// for heap usage. The idea is to always trail reads by one buffer. This is
|
||||
// fine as the device has 1024 byte read buffer. Operations look like this:
|
||||
// W, W, R, W, R, W, R, R
|
||||
// This enables reducing the I/O gaps between USB packets as the device is
|
||||
// always busy with operations.
|
||||
var buf [512]byte
|
||||
cmd := buf[:0]
|
||||
keptCS := false
|
||||
|
||||
// Loop, without increasing the index.
|
||||
for _, p := range pkts {
|
||||
if len(p.W) == 0 && len(p.R) == 0 {
|
||||
continue
|
||||
}
|
||||
// TODO(maruel): s.halfDuplex.
|
||||
|
||||
if !keptCS {
|
||||
for i := 0; i < 5; i++ {
|
||||
cmd = append(cmd, gpioSetD, idle, s.f.dbus.direction)
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
cmd = append(cmd, gpioSetD, start1, s.f.dbus.direction)
|
||||
}
|
||||
}
|
||||
if s.edgeInvert {
|
||||
// This is needed to 'prime' the clock.
|
||||
for i := 0; i < 5; i++ {
|
||||
cmd = append(cmd, gpioSetD, start2, s.f.dbus.direction)
|
||||
}
|
||||
}
|
||||
op := mpsseTxOp(len(p.W) != 0, len(p.R) != 0, ew, er, s.lsbFirst)
|
||||
|
||||
// Do an I/O loop. We can mutate p here because it is a copy.
|
||||
// TODO(maruel): Have the pipeline cross the packet boundary.
|
||||
if len(p.W) == 0 {
|
||||
// Have the write buffer point to the read one. This saves from
|
||||
// allocating memory. The side effect is that it will write whatever
|
||||
// happened to be in the read buffer.
|
||||
p.W = p.R[:]
|
||||
}
|
||||
pendingRead := 0
|
||||
for len(p.W) != 0 {
|
||||
// op, sizelo, sizehi.
|
||||
chunk := len(buf) - 3 - len(cmd)
|
||||
if l := len(p.W); chunk > l {
|
||||
chunk = l
|
||||
}
|
||||
cmd = append(cmd, op, byte(chunk-1), byte((chunk-1)>>8))
|
||||
cmd = append(cmd, p.W[:chunk]...)
|
||||
p.W = p.W[chunk:]
|
||||
if _, err := s.f.h.WriteFast(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
cmd = buf[:0]
|
||||
|
||||
// TODO(maruel): Read 62 bytes at a time?
|
||||
// Delay reading by 512 bytes.
|
||||
if pendingRead >= 512 {
|
||||
if len(p.R) != 0 {
|
||||
// Align reads on 512 bytes exactly, aligned on USB packet size.
|
||||
if _, err := s.f.h.ReadAll(context.Background(), p.R[:512]); err != nil {
|
||||
return err
|
||||
}
|
||||
p.R = p.R[512:]
|
||||
pendingRead -= 512
|
||||
}
|
||||
}
|
||||
pendingRead += chunk
|
||||
}
|
||||
// Do not forget to read whatever is pending.
|
||||
// TODO(maruel): Investigate if a flush helps.
|
||||
if len(p.R) != 0 {
|
||||
// Send a flush to not wait for data.
|
||||
cmd = append(cmd, flush)
|
||||
if _, err := s.f.h.WriteFast(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
cmd = buf[:0]
|
||||
if _, err := s.f.h.ReadAll(context.Background(), p.R); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// TODO(maruel): Inject this in the write if it fits (it will generally
|
||||
// do). That will save one USB I/O, which is not insignificant.
|
||||
keptCS = p.KeepCS
|
||||
if !keptCS {
|
||||
cmd = append(cmd, flush)
|
||||
for i := 0; i < 5; i++ {
|
||||
cmd = append(cmd, gpioSetD, stop, s.f.dbus.direction)
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
cmd = append(cmd, gpioSetD, idle, s.f.dbus.direction)
|
||||
}
|
||||
if _, err := s.f.h.WriteFast(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
cmd = buf[:0]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CLK returns the SCK (clock) pin.
|
||||
func (s *spiMPSEEConn) CLK() gpio.PinOut {
|
||||
return s.f.D0
|
||||
}
|
||||
|
||||
// MOSI returns the SDO (master out, slave in) pin.
|
||||
func (s *spiMPSEEConn) MOSI() gpio.PinOut {
|
||||
return s.f.D1
|
||||
}
|
||||
|
||||
// MISO returns the SDI (master in, slave out) pin.
|
||||
func (s *spiMPSEEConn) MISO() gpio.PinIn {
|
||||
return s.f.D2
|
||||
}
|
||||
|
||||
// CS returns the CSN (chip select) pin.
|
||||
func (s *spiMPSEEConn) CS() gpio.PinOut {
|
||||
return s.f.D3
|
||||
}
|
||||
|
||||
// resetIdle sets D0~D3. D0, D1 and D3 are output but only touch D3 is CS is
|
||||
// used.
|
||||
func (s *spiMPSEEConn) resetIdle() {
|
||||
const clk = byte(1) << 0
|
||||
const mosi = byte(1) << 1
|
||||
const miso = byte(1) << 2
|
||||
const cs = byte(1) << 3
|
||||
if !s.noCS {
|
||||
s.f.dbus.direction &= 0xF0
|
||||
s.f.dbus.direction |= cs
|
||||
s.f.dbus.value &= 0xF0
|
||||
s.f.dbus.value |= cs
|
||||
} else {
|
||||
s.f.dbus.value &= 0xF8
|
||||
s.f.dbus.direction &= 0xF8
|
||||
}
|
||||
s.f.dbus.direction |= mosi | clk
|
||||
if s.clkActiveLow {
|
||||
// Clock idles high.
|
||||
s.f.dbus.value |= clk
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// spiSyncPort is an SPI port over a FTDI device in synchronous bit-bang mode.
|
||||
type spiSyncPort struct {
|
||||
c spiSyncConn
|
||||
|
||||
// Mutable.
|
||||
maxFreq physic.Frequency
|
||||
}
|
||||
|
||||
func (s *spiSyncPort) Close() error {
|
||||
s.c.f.mu.Lock()
|
||||
s.c.f.usingSPI = false
|
||||
s.maxFreq = 0
|
||||
s.c.edgeInvert = false
|
||||
s.c.clkActiveLow = false
|
||||
s.c.noCS = false
|
||||
s.c.lsbFirst = false
|
||||
s.c.halfDuplex = false
|
||||
s.c.f.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *spiSyncPort) String() string {
|
||||
return s.c.f.String()
|
||||
}
|
||||
|
||||
const ft232rMaxSpeed = 3 * physic.MegaHertz
|
||||
|
||||
// Connect implements spi.Port.
|
||||
func (s *spiSyncPort) Connect(f physic.Frequency, m spi.Mode, bits int) (spi.Conn, error) {
|
||||
if f > physic.GigaHertz {
|
||||
return nil, fmt.Errorf("d2xx: invalid speed %s; maximum supported clock is 1.5MHz", f)
|
||||
}
|
||||
if f > ft232rMaxSpeed/2 {
|
||||
// TODO(maruel): Figure out a way to communicate that the speed was lowered.
|
||||
// https://github.com/google/periph/issues/255
|
||||
f = ft232rMaxSpeed / 2
|
||||
}
|
||||
if f < 100*physic.Hertz {
|
||||
return nil, fmt.Errorf("d2xx: invalid speed %s; minimum supported clock is 100Hz; did you forget to multiply by physic.MegaHertz?", f)
|
||||
}
|
||||
if bits&7 != 0 {
|
||||
return nil, errors.New("d2xx: bits must be multiple of 8")
|
||||
}
|
||||
if bits != 8 {
|
||||
return nil, errors.New("d2xx: implement bits per word above 8")
|
||||
}
|
||||
|
||||
s.c.f.mu.Lock()
|
||||
defer s.c.f.mu.Unlock()
|
||||
s.c.noCS = m&spi.NoCS != 0
|
||||
s.c.halfDuplex = m&spi.HalfDuplex != 0
|
||||
s.c.lsbFirst = m&spi.LSBFirst != 0
|
||||
m &^= spi.NoCS | spi.HalfDuplex | spi.LSBFirst
|
||||
if s.c.halfDuplex {
|
||||
return nil, errors.New("d2xx: spi.HalfDuplex is not yet supported (implementing wouldn't be too hard, please submit a PR")
|
||||
}
|
||||
if m < 0 || m > 3 {
|
||||
return nil, errors.New("d2xx: unknown spi mode")
|
||||
}
|
||||
s.c.edgeInvert = m&1 != 0
|
||||
s.c.clkActiveLow = m&2 != 0
|
||||
if s.maxFreq == 0 || f < s.maxFreq {
|
||||
if err := s.c.f.SetSpeed(f * 2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.maxFreq = f
|
||||
}
|
||||
// D0, D2 and D3 are output. D4~D7 are kept as-is.
|
||||
const mosi = byte(1) << 0 // TX
|
||||
const miso = byte(1) << 1 // RX
|
||||
const clk = byte(1) << 2 // RTS
|
||||
const cs = byte(1) << 3 // CTS
|
||||
mask := mosi | clk | cs | (s.c.f.dmask & 0xF0)
|
||||
if err := s.c.f.setDBusMaskLocked(mask); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO(maruel): Combine both following calls if possible. We'd shave off a
|
||||
// few ms.
|
||||
if !s.c.noCS {
|
||||
// CTS/SPI_CS is active low.
|
||||
if err := s.c.f.dbusSyncGPIOOutLocked(3, gpio.High); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if s.c.clkActiveLow {
|
||||
// RTS/SPI_CLK is active low.
|
||||
if err := s.c.f.dbusSyncGPIOOutLocked(2, gpio.High); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
s.c.f.usingSPI = true
|
||||
return &s.c, nil
|
||||
}
|
||||
|
||||
// LimitSpeed implements spi.Port.
|
||||
func (s *spiSyncPort) LimitSpeed(f physic.Frequency) error {
|
||||
if f > physic.GigaHertz {
|
||||
return fmt.Errorf("d2xx: invalid speed %s; maximum supported clock is 1.5MHz", f)
|
||||
}
|
||||
if f < 100*physic.Hertz {
|
||||
return fmt.Errorf("d2xx: invalid speed %s; minimum supported clock is 100Hz; did you forget to multiply by physic.MegaHertz?", f)
|
||||
}
|
||||
s.c.f.mu.Lock()
|
||||
defer s.c.f.mu.Unlock()
|
||||
if s.maxFreq != 0 && s.maxFreq <= f {
|
||||
return nil
|
||||
}
|
||||
if err := s.c.f.SetSpeed(f * 2); err == nil {
|
||||
s.maxFreq = f
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CLK returns the SCK (clock) pin.
|
||||
func (s *spiSyncPort) CLK() gpio.PinOut {
|
||||
return s.c.CLK()
|
||||
}
|
||||
|
||||
// MOSI returns the SDO (master out, slave in) pin.
|
||||
func (s *spiSyncPort) MOSI() gpio.PinOut {
|
||||
return s.c.MOSI()
|
||||
}
|
||||
|
||||
// MISO returns the SDI (master in, slave out) pin.
|
||||
func (s *spiSyncPort) MISO() gpio.PinIn {
|
||||
return s.c.MISO()
|
||||
}
|
||||
|
||||
// CS returns the CSN (chip select) pin.
|
||||
func (s *spiSyncPort) CS() gpio.PinOut {
|
||||
return s.c.CS()
|
||||
}
|
||||
|
||||
type spiSyncConn struct {
|
||||
// Immutable.
|
||||
f *FT232R
|
||||
|
||||
// Initialized at Connect().
|
||||
edgeInvert bool // CPHA=1
|
||||
clkActiveLow bool // CPOL=1
|
||||
noCS bool // CS line is not changed
|
||||
lsbFirst bool // Default is MSB first
|
||||
halfDuplex bool // 3 wire mode
|
||||
}
|
||||
|
||||
func (s *spiSyncConn) String() string {
|
||||
return s.f.String()
|
||||
}
|
||||
|
||||
func (s *spiSyncConn) Tx(w, r []byte) error {
|
||||
var p = [1]spi.Packet{{W: w, R: r}}
|
||||
return s.TxPackets(p[:])
|
||||
}
|
||||
|
||||
func (s *spiSyncConn) Duplex() conn.Duplex {
|
||||
// TODO(maruel): Support half if there's a need.
|
||||
return conn.Full
|
||||
}
|
||||
|
||||
func (s *spiSyncConn) TxPackets(pkts []spi.Packet) error {
|
||||
// We need to 'expand' each bit 2 times * 8 bits, which leads
|
||||
// to a 16x memory usage increase. Adds 5 samples before and after.
|
||||
totalW := 0
|
||||
totalR := 0
|
||||
for _, p := range pkts {
|
||||
if p.KeepCS {
|
||||
return errors.New("d2xx: implement spi.Packet.KeepCS")
|
||||
}
|
||||
if p.BitsPerWord&7 != 0 {
|
||||
return errors.New("d2xx: bits must be a multiple of 8")
|
||||
}
|
||||
if p.BitsPerWord != 0 && p.BitsPerWord != 8 {
|
||||
return errors.New("d2xx: implement spi.Packet.BitsPerWord")
|
||||
}
|
||||
if err := verifyBuffers(p.W, p.R); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(maruel): Correctly calculate offsets.
|
||||
if len(p.W) != 0 {
|
||||
totalW += 2 * 8 * len(p.W)
|
||||
}
|
||||
if len(p.R) != 0 {
|
||||
totalR += 2 * 8 * len(p.R)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a large, single chunk.
|
||||
var we, re []byte
|
||||
if totalW != 0 {
|
||||
totalW += 10
|
||||
we = make([]byte, 0, totalW)
|
||||
}
|
||||
if totalR != 0 {
|
||||
totalR += 10
|
||||
re = make([]byte, totalR)
|
||||
}
|
||||
const mosi = byte(1) << 0 // TX
|
||||
const miso = byte(1) << 1 // RX
|
||||
const clk = byte(1) << 2 // RTS
|
||||
const cs = byte(1) << 3 // CTS
|
||||
|
||||
s.f.mu.Lock()
|
||||
defer s.f.mu.Unlock()
|
||||
|
||||
// https://en.wikipedia.org/wiki/Serial_Peripheral_Interface#Data_transmission
|
||||
|
||||
csActive := s.f.dvalue & s.f.dmask & 0xF0
|
||||
csIdle := csActive
|
||||
if !s.noCS {
|
||||
csIdle = csActive | cs
|
||||
}
|
||||
clkIdle := csActive
|
||||
clkActive := clkIdle | clk
|
||||
if s.clkActiveLow {
|
||||
clkActive, clkIdle = clkIdle, clkActive
|
||||
csIdle |= clk
|
||||
}
|
||||
// Start of tx; assert CS if needed.
|
||||
we = append(we, csIdle, clkIdle, clkIdle, clkIdle, clkIdle)
|
||||
for _, p := range pkts {
|
||||
if len(p.W) == 0 && len(p.R) == 0 {
|
||||
continue
|
||||
}
|
||||
// TODO(maruel): s.halfDuplex.
|
||||
for _, b := range p.W {
|
||||
for j := uint(0); j < 8; j++ {
|
||||
// For each bit, handle clock phase and data phase.
|
||||
bit := byte(0)
|
||||
if !s.lsbFirst {
|
||||
// MSBF
|
||||
if b&(0x80>>j) != 0 {
|
||||
bit = mosi
|
||||
}
|
||||
} else {
|
||||
// LSBF
|
||||
if b&(1<<j) != 0 {
|
||||
bit = mosi
|
||||
}
|
||||
}
|
||||
if !s.edgeInvert {
|
||||
// Mode0/2; CPHA=0
|
||||
we = append(we, clkIdle|bit, clkActive|bit)
|
||||
} else {
|
||||
// Mode1/3; CPHA=1
|
||||
we = append(we, clkActive|bit, clkIdle|bit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// End of tx; deassert CS.
|
||||
we = append(we, clkIdle, clkIdle, clkIdle, clkIdle, csIdle)
|
||||
|
||||
if err := s.f.txLocked(we, re); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Extract data from re into r.
|
||||
for _, p := range pkts {
|
||||
// TODO(maruel): Correctly calculate offsets.
|
||||
if len(p.W) == 0 && len(p.R) == 0 {
|
||||
continue
|
||||
}
|
||||
// TODO(maruel): halfDuplex.
|
||||
for i := range p.R {
|
||||
// For each bit, read at the right data phase.
|
||||
b := byte(0)
|
||||
for j := 0; j < 8; j++ {
|
||||
if re[5+i*8*2+j*2+1]&byte(1)<<1 != 0 {
|
||||
if !s.lsbFirst {
|
||||
// MSBF
|
||||
b |= 0x80 >> uint(j)
|
||||
} else {
|
||||
// LSBF
|
||||
b |= 1 << uint(j)
|
||||
}
|
||||
}
|
||||
}
|
||||
p.R[i] = b
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CLK returns the SCK (clock) pin.
|
||||
func (s *spiSyncConn) CLK() gpio.PinOut {
|
||||
return s.f.D2 // RTS
|
||||
}
|
||||
|
||||
// MOSI returns the SDO (master out, slave in) pin.
|
||||
func (s *spiSyncConn) MOSI() gpio.PinOut {
|
||||
return s.f.D0 // TX
|
||||
}
|
||||
|
||||
// MISO returns the SDI (master in, slave out) pin.
|
||||
func (s *spiSyncConn) MISO() gpio.PinIn {
|
||||
return s.f.D1 // RX
|
||||
}
|
||||
|
||||
// CS returns the CSN (chip select) pin.
|
||||
func (s *spiSyncConn) CS() gpio.PinOut {
|
||||
return s.f.D3 // CTS
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
func verifyBuffers(w, r []byte) error {
|
||||
if len(w) != 0 {
|
||||
if len(r) != 0 {
|
||||
if len(w) != len(r) {
|
||||
return errors.New("d2xx: both buffers must have the same size")
|
||||
}
|
||||
}
|
||||
// TODO(maruel): When the buffer is >64Kb, cut it in parts and do not
|
||||
// request a flush. Still try to read though.
|
||||
if len(w) > 65536 {
|
||||
return errors.New("d2xx: maximum buffer size is 64Kb")
|
||||
}
|
||||
} else if len(r) != 0 {
|
||||
// TODO(maruel): Remove, this is not a problem.
|
||||
if len(r) > 65536 {
|
||||
return errors.New("d2xx: maximum buffer size is 64Kb")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ spi.PortCloser = &spiMPSEEPort{}
|
||||
var _ spi.Conn = &spiMPSEEConn{}
|
||||
var _ spi.PortCloser = &spiSyncPort{}
|
||||
var _ spi.Conn = &spiSyncConn{}
|
19
vendor/periph.io/x/host/v3/host.go
generated
vendored
Normal file
19
vendor/periph.io/x/host/v3/host.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package host
|
||||
|
||||
import (
|
||||
"periph.io/x/conn/v3/driver/driverreg"
|
||||
_ "periph.io/x/host/v3/ftdi"
|
||||
)
|
||||
|
||||
// Init calls driverreg.Init() and returns it as-is.
|
||||
//
|
||||
// The only difference is that by calling host.Init(), you are guaranteed to
|
||||
// have all the host drivers implemented in this library to be implicitly
|
||||
// loaded.
|
||||
func Init() (*driverreg.State, error) {
|
||||
return driverreg.Init()
|
||||
}
|
21
vendor/periph.io/x/host/v3/host_arm.go
generated
vendored
Normal file
21
vendor/periph.io/x/host/v3/host_arm.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package host
|
||||
|
||||
import (
|
||||
// Make sure CPU and board drivers are registered.
|
||||
_ "periph.io/x/host/v3/allwinner"
|
||||
_ "periph.io/x/host/v3/am335x"
|
||||
_ "periph.io/x/host/v3/bcm283x"
|
||||
_ "periph.io/x/host/v3/beagle/bone"
|
||||
_ "periph.io/x/host/v3/beagle/green"
|
||||
_ "periph.io/x/host/v3/chip"
|
||||
_ "periph.io/x/host/v3/odroidc1"
|
||||
|
||||
// While this board is ARM64, it may run ARM 32 bits binaries so load it on
|
||||
// 32 bits builds too.
|
||||
_ "periph.io/x/host/v3/pine64"
|
||||
_ "periph.io/x/host/v3/rpi"
|
||||
)
|
13
vendor/periph.io/x/host/v3/host_arm64.go
generated
vendored
Normal file
13
vendor/periph.io/x/host/v3/host_arm64.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package host
|
||||
|
||||
import (
|
||||
// Make sure CPU and board drivers are registered.
|
||||
_ "periph.io/x/host/v3/allwinner"
|
||||
_ "periph.io/x/host/v3/bcm283x"
|
||||
_ "periph.io/x/host/v3/pine64"
|
||||
_ "periph.io/x/host/v3/rpi"
|
||||
)
|
10
vendor/periph.io/x/host/v3/host_linux.go
generated
vendored
Normal file
10
vendor/periph.io/x/host/v3/host_linux.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package host
|
||||
|
||||
import (
|
||||
// Make sure sysfs drivers are registered.
|
||||
_ "periph.io/x/host/v3/sysfs"
|
||||
)
|
26
vendor/periph.io/x/host/v3/odroidc1/doc.go
generated
vendored
Normal file
26
vendor/periph.io/x/host/v3/odroidc1/doc.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package odroidc1 contains header definitions for Hardkernel's ODROID C0, C1,
|
||||
// and C1+ boards.
|
||||
//
|
||||
// These boards use an Amlogic S805 processor (called "meson_8b" in the linux
|
||||
// kernel). Currently no package for memory-mapped I/O has been written for
|
||||
// this processor, thus all gpio functions are implemented via sysfs.
|
||||
//
|
||||
// This package only exports the main J2 header, which is rPi compatible except
|
||||
// for a couple of analog pins (which are not currently supported). The J2
|
||||
// header has two I²C buses on header pins 3/5 and 27/28, the I²C functionality
|
||||
// can be enabled by loading the aml_i2c kernel module. It has one SPI bus on
|
||||
// header pins 19/21/23/24. The onewire gpio driver appears to be loaded by
|
||||
// default on header pin 7.
|
||||
//
|
||||
// References
|
||||
//
|
||||
// Product page: http://www.hardkernel.com/main/products/prdt_info.php?g_code=G143703355573&tab_idx=2
|
||||
//
|
||||
// Hardware wiki: http://odroid.com/dokuwiki/doku.php?id=en:c1_hardware
|
||||
//
|
||||
// Ubuntu drivers: http://odroid.com/dokuwiki/doku.php?id=en:odroid-c1#ubuntu
|
||||
package odroidc1
|
195
vendor/periph.io/x/host/v3/odroidc1/odroidc1.go
generated
vendored
Normal file
195
vendor/periph.io/x/host/v3/odroidc1/odroidc1.go
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package odroidc1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"periph.io/x/conn/v3/driver/driverreg"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/gpio/gpioreg"
|
||||
"periph.io/x/conn/v3/pin"
|
||||
"periph.io/x/conn/v3/pin/pinreg"
|
||||
"periph.io/x/host/v3/distro"
|
||||
"periph.io/x/host/v3/sysfs"
|
||||
)
|
||||
|
||||
// The J2 header is rPi compatible, except for the two analog pins and the 1.8V
|
||||
// output.
|
||||
var (
|
||||
J2_1 = pin.V3_3 // 3.3V; max 30mA
|
||||
J2_2 = pin.V5 // 5V (after filtering)
|
||||
J2_3 gpio.PinIO = gpio.INVALID // I2C1_SDA
|
||||
J2_4 = pin.V5 // 5V (after filtering)
|
||||
J2_5 gpio.PinIO = gpio.INVALID // I2C1_SCL
|
||||
J2_6 = pin.GROUND //
|
||||
J2_7 gpio.PinIO = gpio.INVALID // CLK0
|
||||
J2_8 gpio.PinIO = gpio.INVALID // UART0_TX, UART1_TX
|
||||
J2_9 = pin.GROUND //
|
||||
J2_10 gpio.PinIO = gpio.INVALID // UART0_RX, UART1_RX
|
||||
J2_11 gpio.PinIO = gpio.INVALID // UART0_RTS, SPI1_CS1, UART1_RTS
|
||||
J2_12 gpio.PinIO = gpio.INVALID // I2S_SCK, SPI1_CS0, PWM0
|
||||
J2_13 gpio.PinIO = gpio.INVALID // GPIO116
|
||||
J2_14 = pin.GROUND //
|
||||
J2_15 gpio.PinIO = gpio.INVALID // GPIO115
|
||||
J2_16 gpio.PinIO = gpio.INVALID // GPIO104
|
||||
J2_17 = pin.V3_3 //
|
||||
J2_18 gpio.PinIO = gpio.INVALID // GPIO102
|
||||
J2_19 gpio.PinIO = gpio.INVALID // SPI0_MOSI
|
||||
J2_20 = pin.GROUND //
|
||||
J2_21 gpio.PinIO = gpio.INVALID // SPI0_MISO
|
||||
J2_22 gpio.PinIO = gpio.INVALID // GPIO103
|
||||
J2_23 gpio.PinIO = gpio.INVALID // SPI0_CLK
|
||||
J2_24 gpio.PinIO = gpio.INVALID // SPI0_CS0
|
||||
J2_25 = pin.GROUND //
|
||||
J2_26 gpio.PinIO = gpio.INVALID // SPI0_CS1
|
||||
J2_27 gpio.PinIO = gpio.INVALID // I2C0_SDA
|
||||
J2_28 gpio.PinIO = gpio.INVALID // I2C0_SCL
|
||||
J2_29 gpio.PinIO = gpio.INVALID // CLK1
|
||||
J2_30 = pin.GROUND //
|
||||
J2_31 gpio.PinIO = gpio.INVALID // CLK2
|
||||
J2_32 gpio.PinIO = gpio.INVALID // PWM0
|
||||
J2_33 gpio.PinIO = gpio.INVALID // PWM1
|
||||
J2_34 = pin.GROUND //
|
||||
J2_35 gpio.PinIO = gpio.INVALID // I2S_WS, SPI1_MISO, PWM1
|
||||
J2_36 gpio.PinIO = gpio.INVALID // UART0_CTS, SPI1_CS2, UART1_CTS
|
||||
J2_37 = pin.INVALID // BUG(tve): make pins J2_37 and J2_40 functional once analog support is implemented
|
||||
J2_38 = pin.V1_8 //
|
||||
J2_39 = pin.GROUND //
|
||||
J2_40 = pin.INVALID // See above.
|
||||
)
|
||||
|
||||
// Present returns true if running on a Hardkernel ODROID-C0/C1/C1+ board.
|
||||
//
|
||||
// It looks for "8726_M8B" in the device tree or "ODROIDC" in cpuinfo. The
|
||||
// following information is expected in the device dtree:
|
||||
// root@odroid:/proc/device-tree# od -c compatible
|
||||
// 0000000 A M L O G I C , 8 7 2 6 _ M 8 B
|
||||
func Present() bool {
|
||||
for _, c := range distro.DTCompatible() {
|
||||
if strings.Contains(c, "8726_M8B") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return distro.CPUInfo()["Hardware"] == "ODROIDC"
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// aliases is a list of aliases for the various gpio pins, this allows users to
|
||||
// refer to pins using the documented and labeled names instead of some GPIOnnn
|
||||
// name. The map key is the alias and the value is the real pin name.
|
||||
var aliases = map[string]int{
|
||||
"I2C0_SDA": 74,
|
||||
"I2C0_SCL": 75,
|
||||
"I2C1_SDA": 76,
|
||||
"I2C1_SCL": 77,
|
||||
"I2CA_SDA": 74,
|
||||
"I2CA_SCL": 75,
|
||||
"I2CB_SDA": 76,
|
||||
"I2CB_SCL": 77,
|
||||
"SPI0_MOSI": 107,
|
||||
"SPI0_MISO": 106,
|
||||
"SPI0_CLK": 105,
|
||||
"SPI0_CS0": 117,
|
||||
}
|
||||
|
||||
// sysfsPin is a safe way to get a sysfs pin
|
||||
func sysfsPin(n int) gpio.PinIO {
|
||||
if p, ok := sysfs.Pins[n]; ok {
|
||||
return p
|
||||
}
|
||||
return gpio.INVALID
|
||||
}
|
||||
|
||||
// driver implements drivers.Driver.
|
||||
type driver struct {
|
||||
}
|
||||
|
||||
func (d *driver) String() string {
|
||||
return "odroid-c1"
|
||||
}
|
||||
|
||||
func (d *driver) Prerequisites() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) After() []string {
|
||||
return []string{"sysfs-gpio"}
|
||||
}
|
||||
|
||||
func (d *driver) Init() (bool, error) {
|
||||
if !Present() {
|
||||
return false, errors.New("board Hardkernel ODROID-C0/C1/C1+ not detected")
|
||||
}
|
||||
J2_3 = sysfsPin(74)
|
||||
J2_5 = sysfsPin(75)
|
||||
J2_7 = sysfsPin(83) // usually taken by 1-wire driver
|
||||
J2_8 = sysfsPin(113) // usually not available
|
||||
J2_10 = sysfsPin(114) // usually not available
|
||||
J2_11 = sysfsPin(88)
|
||||
J2_12 = sysfsPin(87)
|
||||
J2_13 = sysfsPin(116)
|
||||
J2_15 = sysfsPin(115)
|
||||
J2_16 = sysfsPin(104)
|
||||
J2_18 = sysfsPin(102)
|
||||
J2_19 = sysfsPin(107)
|
||||
J2_21 = sysfsPin(106)
|
||||
J2_22 = sysfsPin(103)
|
||||
J2_23 = sysfsPin(105)
|
||||
J2_24 = sysfsPin(117)
|
||||
J2_26 = sysfsPin(118)
|
||||
J2_27 = sysfsPin(76)
|
||||
J2_28 = sysfsPin(77)
|
||||
J2_29 = sysfsPin(101)
|
||||
J2_31 = sysfsPin(100)
|
||||
J2_32 = sysfsPin(99)
|
||||
J2_33 = sysfsPin(108)
|
||||
J2_35 = sysfsPin(97)
|
||||
J2_36 = sysfsPin(98)
|
||||
|
||||
// J2 is the 40-pin rPi-compatible header
|
||||
J2 := [][]pin.Pin{
|
||||
{J2_1, J2_2},
|
||||
{J2_3, J2_4},
|
||||
{J2_5, J2_6},
|
||||
{J2_7, J2_8},
|
||||
{J2_9, J2_10},
|
||||
{J2_11, J2_12},
|
||||
{J2_13, J2_14},
|
||||
{J2_15, J2_16},
|
||||
{J2_17, J2_18},
|
||||
{J2_19, J2_20},
|
||||
{J2_21, J2_22},
|
||||
{J2_23, J2_24},
|
||||
{J2_25, J2_26},
|
||||
{J2_27, J2_28},
|
||||
{J2_29, J2_30},
|
||||
{J2_31, J2_32},
|
||||
{J2_33, J2_34},
|
||||
{J2_35, J2_36},
|
||||
{J2_37, J2_38},
|
||||
{J2_39, J2_40},
|
||||
}
|
||||
if err := pinreg.Register("J2", J2); err != nil {
|
||||
return true, err
|
||||
}
|
||||
for alias, number := range aliases {
|
||||
if err := gpioreg.RegisterAlias(alias, strconv.Itoa(number)); err != nil {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
if isArm {
|
||||
driverreg.MustRegister(&drv)
|
||||
}
|
||||
}
|
||||
|
||||
var drv driver
|
7
vendor/periph.io/x/host/v3/odroidc1/odroidc1_arm.go
generated
vendored
Normal file
7
vendor/periph.io/x/host/v3/odroidc1/odroidc1_arm.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2016 The PIO Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package odroidc1
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/odroidc1/odroidc1_other.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/odroidc1/odroidc1_other.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The PIO Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm
|
||||
|
||||
package odroidc1
|
||||
|
||||
const isArm = false
|
15
vendor/periph.io/x/host/v3/pine64/doc.go
generated
vendored
Normal file
15
vendor/periph.io/x/host/v3/pine64/doc.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package pine64 contains Pine64 hardware logic. It is intrinsically
|
||||
// related to package a64.
|
||||
//
|
||||
// Requires Armbian Jessie Server.
|
||||
//
|
||||
// Physical
|
||||
//
|
||||
// http://files.pine64.org/doc/Pine%20A64%20Schematic/Pine%20A64%20Pin%20Assignment%20160119.pdf
|
||||
//
|
||||
// http://wiki.pine64.org/images/2/2e/Pine64_Board_Connector_heatsink.png
|
||||
package pine64
|
271
vendor/periph.io/x/host/v3/pine64/pine64.go
generated
vendored
Normal file
271
vendor/periph.io/x/host/v3/pine64/pine64.go
generated
vendored
Normal file
@ -0,0 +1,271 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package pine64
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"periph.io/x/conn/v3/driver/driverreg"
|
||||
"periph.io/x/conn/v3/pin"
|
||||
"periph.io/x/conn/v3/pin/pinreg"
|
||||
"periph.io/x/host/v3/allwinner"
|
||||
"periph.io/x/host/v3/distro"
|
||||
)
|
||||
|
||||
// Present returns true if running on a Pine64 board.
|
||||
//
|
||||
// https://www.pine64.org/
|
||||
func Present() bool {
|
||||
if isArm {
|
||||
return strings.HasPrefix(distro.DTModel(), "Pine64")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Pine64 specific pins.
|
||||
var (
|
||||
VCC = &pin.BasicPin{N: "VCC"} //
|
||||
IOVCC = &pin.BasicPin{N: "IOVCC"} // Power supply for port A
|
||||
TEMP_SENSOR = &pin.BasicPin{N: "TEMP_SENSOR"} //
|
||||
IR_RX = &pin.BasicPin{N: "IR_RX"} // IR Data Receive
|
||||
CHARGER_LED = &pin.BasicPin{N: "CHARGER_LED"} //
|
||||
RESET = &pin.BasicPin{N: "RESET"} //
|
||||
PWR_SWITCH = &pin.BasicPin{N: "PWR_SWITCH "} //
|
||||
)
|
||||
|
||||
// All the individual pins on the headers.
|
||||
var (
|
||||
P1_1 = pin.V3_3 // max 40mA
|
||||
P1_2 = pin.V5 // (filtered)
|
||||
P1_3 = allwinner.PH3 //
|
||||
P1_4 = pin.V5 // (filtered)
|
||||
P1_5 = allwinner.PH2 //
|
||||
P1_6 = pin.GROUND //
|
||||
P1_7 = allwinner.PL10 //
|
||||
P1_8 = allwinner.PB0 //
|
||||
P1_9 = pin.GROUND //
|
||||
P1_10 = allwinner.PB1 //
|
||||
P1_11 = allwinner.PC7 //
|
||||
P1_12 = allwinner.PC8 //
|
||||
P1_13 = allwinner.PH9 //
|
||||
P1_14 = pin.GROUND //
|
||||
P1_15 = allwinner.PC12 //
|
||||
P1_16 = allwinner.PC13 //
|
||||
P1_17 = pin.V3_3 //
|
||||
P1_18 = allwinner.PC14 //
|
||||
P1_19 = allwinner.PC0 //
|
||||
P1_20 = pin.GROUND //
|
||||
P1_21 = allwinner.PC1 //
|
||||
P1_22 = allwinner.PC15 //
|
||||
P1_23 = allwinner.PC2 //
|
||||
P1_24 = allwinner.PC3 //
|
||||
P1_25 = pin.GROUND //
|
||||
P1_26 = allwinner.PH7 //
|
||||
P1_27 = allwinner.PL9 //
|
||||
P1_28 = allwinner.PL8 //
|
||||
P1_29 = allwinner.PH5 //
|
||||
P1_30 = pin.GROUND //
|
||||
P1_31 = allwinner.PH6 //
|
||||
P1_32 = allwinner.PC4 //
|
||||
P1_33 = allwinner.PC5 //
|
||||
P1_34 = pin.GROUND //
|
||||
P1_35 = allwinner.PC9 //
|
||||
P1_36 = allwinner.PC6 //
|
||||
P1_37 = allwinner.PC16 //
|
||||
P1_38 = allwinner.PC10 //
|
||||
P1_39 = pin.GROUND //
|
||||
P1_40 = allwinner.PC11 //
|
||||
|
||||
EULER_1 = pin.V3_3 //
|
||||
EULER_2 = pin.DC_IN //
|
||||
EULER_3 = pin.BAT_PLUS //
|
||||
EULER_4 = pin.DC_IN //
|
||||
EULER_5 = TEMP_SENSOR //
|
||||
EULER_6 = pin.GROUND //
|
||||
EULER_7 = IR_RX //
|
||||
EULER_8 = pin.V5 //
|
||||
EULER_9 = pin.GROUND //
|
||||
EULER_10 = allwinner.PH8 //
|
||||
EULER_11 = allwinner.PB3 //
|
||||
EULER_12 = allwinner.PB4 //
|
||||
EULER_13 = allwinner.PB5 //
|
||||
EULER_14 = pin.GROUND //
|
||||
EULER_15 = allwinner.PB6 //
|
||||
EULER_16 = allwinner.PB7 //
|
||||
EULER_17 = pin.V3_3 //
|
||||
EULER_18 = allwinner.PD4 //
|
||||
EULER_19 = allwinner.PD2 //
|
||||
EULER_20 = pin.GROUND //
|
||||
EULER_21 = allwinner.PD3 //
|
||||
EULER_22 = allwinner.PD5 //
|
||||
EULER_23 = allwinner.PD1 //
|
||||
EULER_24 = allwinner.PD0 //
|
||||
EULER_25 = pin.GROUND //
|
||||
EULER_26 = allwinner.PD6 //
|
||||
EULER_27 = allwinner.PB2 //
|
||||
EULER_28 = allwinner.PD7 //
|
||||
EULER_29 = allwinner.PB8 //
|
||||
EULER_30 = allwinner.PB9 //
|
||||
EULER_31 = allwinner.EAROUTP //
|
||||
EULER_32 = allwinner.EAROUTN //
|
||||
EULER_33 = pin.INVALID //
|
||||
EULER_34 = pin.GROUND //
|
||||
|
||||
EXP_1 = pin.V3_3 //
|
||||
EXP_2 = allwinner.PL7 //
|
||||
EXP_3 = CHARGER_LED //
|
||||
EXP_4 = RESET //
|
||||
EXP_5 = PWR_SWITCH //
|
||||
EXP_6 = pin.GROUND //
|
||||
EXP_7 = allwinner.PB8 //
|
||||
EXP_8 = allwinner.PB9 //
|
||||
EXP_9 = pin.GROUND //
|
||||
EXP_10 = allwinner.KEY_ADC //
|
||||
|
||||
WIFI_BT_1 = pin.GROUND //
|
||||
WIFI_BT_2 = allwinner.PG6 //
|
||||
WIFI_BT_3 = allwinner.PG0 //
|
||||
WIFI_BT_4 = allwinner.PG7 //
|
||||
WIFI_BT_5 = pin.GROUND //
|
||||
WIFI_BT_6 = allwinner.PG8 //
|
||||
WIFI_BT_7 = allwinner.PG1 //
|
||||
WIFI_BT_8 = allwinner.PG9 //
|
||||
WIFI_BT_9 = allwinner.PG2 //
|
||||
WIFI_BT_10 = allwinner.PG10 //
|
||||
WIFI_BT_11 = allwinner.PG3 //
|
||||
WIFI_BT_12 = allwinner.PG11 //
|
||||
WIFI_BT_13 = allwinner.PG4 //
|
||||
WIFI_BT_14 = allwinner.PG12 //
|
||||
WIFI_BT_15 = allwinner.PG5 //
|
||||
WIFI_BT_16 = allwinner.PG13 //
|
||||
WIFI_BT_17 = allwinner.PL2 //
|
||||
WIFI_BT_18 = pin.GROUND //
|
||||
WIFI_BT_19 = allwinner.PL3 //
|
||||
WIFI_BT_20 = allwinner.PL5 //
|
||||
WIFI_BT_21 = allwinner.X32KFOUT //
|
||||
WIFI_BT_22 = allwinner.PL5 //
|
||||
WIFI_BT_23 = pin.GROUND //
|
||||
WIFI_BT_24 = allwinner.PL6 //
|
||||
WIFI_BT_25 = VCC //
|
||||
WIFI_BT_26 = IOVCC //
|
||||
|
||||
AUDIO_LEFT = pin.INVALID // BUG(maruel): Fix once analog is implemented.
|
||||
AUDIO_RIGHT = pin.INVALID //
|
||||
)
|
||||
|
||||
//
|
||||
|
||||
// driver implements periph.Driver.
|
||||
type driver struct {
|
||||
}
|
||||
|
||||
func (d *driver) String() string {
|
||||
return "pine64"
|
||||
}
|
||||
|
||||
func (d *driver) Prerequisites() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) After() []string {
|
||||
return []string{"allwinner-gpio", "allwinner-gpio-pl"}
|
||||
}
|
||||
|
||||
func (d *driver) Init() (bool, error) {
|
||||
if !Present() {
|
||||
return false, errors.New("pine64 board not detected")
|
||||
}
|
||||
if err := pinreg.Register("P1", [][]pin.Pin{
|
||||
{P1_1, P1_2},
|
||||
{P1_3, P1_4},
|
||||
{P1_5, P1_6},
|
||||
{P1_7, P1_8},
|
||||
{P1_9, P1_10},
|
||||
{P1_11, P1_12},
|
||||
{P1_13, P1_14},
|
||||
{P1_15, P1_16},
|
||||
{P1_17, P1_18},
|
||||
{P1_19, P1_20},
|
||||
{P1_21, P1_22},
|
||||
{P1_23, P1_24},
|
||||
{P1_25, P1_26},
|
||||
{P1_27, P1_28},
|
||||
{P1_29, P1_30},
|
||||
{P1_31, P1_32},
|
||||
{P1_33, P1_34},
|
||||
{P1_35, P1_36},
|
||||
{P1_37, P1_38},
|
||||
{P1_39, P1_40},
|
||||
}); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if err := pinreg.Register("EULER", [][]pin.Pin{
|
||||
{EULER_1, EULER_2},
|
||||
{EULER_3, EULER_4},
|
||||
{EULER_5, EULER_6},
|
||||
{EULER_7, EULER_8},
|
||||
{EULER_9, EULER_10},
|
||||
{EULER_11, EULER_12},
|
||||
{EULER_13, EULER_14},
|
||||
{EULER_15, EULER_16},
|
||||
{EULER_17, EULER_18},
|
||||
{EULER_19, EULER_20},
|
||||
{EULER_21, EULER_22},
|
||||
{EULER_23, EULER_24},
|
||||
{EULER_25, EULER_26},
|
||||
{EULER_27, EULER_28},
|
||||
{EULER_29, EULER_30},
|
||||
{EULER_31, EULER_32},
|
||||
{EULER_33, EULER_34},
|
||||
}); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
if err := pinreg.Register("EXP", [][]pin.Pin{
|
||||
{EXP_1, EXP_2},
|
||||
{EXP_3, EXP_4},
|
||||
{EXP_5, EXP_6},
|
||||
{EXP_7, EXP_8},
|
||||
{EXP_9, EXP_10},
|
||||
}); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
if err := pinreg.Register("WIFI_BT", [][]pin.Pin{
|
||||
{WIFI_BT_1, WIFI_BT_2},
|
||||
{WIFI_BT_3, WIFI_BT_4},
|
||||
{WIFI_BT_5, WIFI_BT_6},
|
||||
{WIFI_BT_7, WIFI_BT_8},
|
||||
{WIFI_BT_9, WIFI_BT_10},
|
||||
{WIFI_BT_11, WIFI_BT_12},
|
||||
{WIFI_BT_13, WIFI_BT_14},
|
||||
{WIFI_BT_15, WIFI_BT_16},
|
||||
{WIFI_BT_17, WIFI_BT_18},
|
||||
{WIFI_BT_19, WIFI_BT_20},
|
||||
{WIFI_BT_21, WIFI_BT_22},
|
||||
{WIFI_BT_23, WIFI_BT_24},
|
||||
{WIFI_BT_25, WIFI_BT_26},
|
||||
}); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
if err := pinreg.Register("AUDIO", [][]pin.Pin{
|
||||
{AUDIO_LEFT},
|
||||
{AUDIO_RIGHT},
|
||||
}); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
if isArm {
|
||||
driverreg.MustRegister(&drv)
|
||||
}
|
||||
}
|
||||
|
||||
var drv driver
|
7
vendor/periph.io/x/host/v3/pine64/pine64_arm.go
generated
vendored
Normal file
7
vendor/periph.io/x/host/v3/pine64/pine64_arm.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package pine64
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/pine64/pine64_arm64.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/pine64/pine64_arm64.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build arm64
|
||||
|
||||
package pine64
|
||||
|
||||
const isArm = true
|
9
vendor/periph.io/x/host/v3/pine64/pine64_other.go
generated
vendored
Normal file
9
vendor/periph.io/x/host/v3/pine64/pine64_other.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm,!arm64
|
||||
|
||||
package pine64
|
||||
|
||||
const isArm = false
|
182
vendor/periph.io/x/host/v3/pmem/alloc.go
generated
vendored
Normal file
182
vendor/periph.io/x/host/v3/pmem/alloc.go
generated
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package pmem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const pageSize = 4096
|
||||
|
||||
// Mem represents a section of memory that is usable by the DMA controller.
|
||||
//
|
||||
// Since this is physically allocated memory, that could potentially have been
|
||||
// allocated in spite of OS consent, for example by asking the GPU directly, it
|
||||
// is important to call Close() before process exit.
|
||||
type Mem interface {
|
||||
io.Closer
|
||||
// Bytes returns the user space memory mapped buffer address as a slice of
|
||||
// bytes.
|
||||
//
|
||||
// It is the raw view of the memory from this process.
|
||||
Bytes() []byte
|
||||
// AsPOD initializes a pointer to a POD (plain old data) to point to the
|
||||
// memory mapped region.
|
||||
//
|
||||
// pp must be a pointer to:
|
||||
//
|
||||
// - pointer to a base size type (uint8, int64, float32, etc)
|
||||
// - struct
|
||||
// - array of the above
|
||||
// - slice of the above
|
||||
//
|
||||
// and the value must be nil. Returns an error otherwise.
|
||||
//
|
||||
// If a pointer to a slice is passed in, it is initialized to the length and
|
||||
// capacity set to the maximum number of elements this slice can represent.
|
||||
//
|
||||
// The pointer initialized points to the same address as Bytes().
|
||||
AsPOD(pp interface{}) error
|
||||
// PhysAddr is the physical address. It can be either 32 bits or 64 bits,
|
||||
// depending on the bitness of the OS kernel, not on the user mode build,
|
||||
// e.g. you could have compiled on a 32 bits Go toolchain but running on a
|
||||
// 64 bits kernel.
|
||||
PhysAddr() uint64
|
||||
}
|
||||
|
||||
// MemAlloc represents contiguous physically locked memory that was allocated.
|
||||
//
|
||||
// The memory is mapped in user space.
|
||||
//
|
||||
// MemAlloc implements Mem.
|
||||
type MemAlloc struct {
|
||||
View
|
||||
}
|
||||
|
||||
// Close unmaps the physical memory allocation.
|
||||
func (m *MemAlloc) Close() error {
|
||||
if err := munlock(m.orig); err != nil {
|
||||
return err
|
||||
}
|
||||
return munmap(m.orig)
|
||||
}
|
||||
|
||||
// Alloc allocates a continuous chunk of physical memory.
|
||||
//
|
||||
// Size must be rounded to 4Kb. Allocations of 4Kb will normally succeed.
|
||||
// Allocations larger than 64Kb will likely fail due to kernel memory
|
||||
// fragmentation; rebooting the host or reducing the number of running programs
|
||||
// may help.
|
||||
//
|
||||
// The allocated memory is uncached.
|
||||
func Alloc(size int) (*MemAlloc, error) {
|
||||
if size == 0 || size&(pageSize-1) != 0 {
|
||||
return nil, wrapf("allocated memory must be rounded to %d bytes", pageSize)
|
||||
}
|
||||
if isLinux && !isWSL() {
|
||||
return allocLinux(size)
|
||||
}
|
||||
return nil, wrapf("memory allocation is not supported on this platform")
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var (
|
||||
wslOnce sync.Once
|
||||
isWSLValue bool
|
||||
)
|
||||
|
||||
// uallocMemLocked allocates user space memory and requests the OS to have the
|
||||
// chunk to be locked into physical memory.
|
||||
func uallocMemLocked(size int) ([]byte, error) {
|
||||
// It is important to write to the memory so it is forced to be present.
|
||||
b, err := uallocMem(size)
|
||||
if err == nil {
|
||||
for i := range b {
|
||||
b[i] = 0
|
||||
}
|
||||
if err = mlock(b); err != nil {
|
||||
// Ignore the unmap error.
|
||||
_ = munmap(b)
|
||||
return nil, wrapf("locking %d bytes failed: %v", size, err)
|
||||
}
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
|
||||
// allocLinux allocates physical memory and returns a user view to it.
|
||||
func allocLinux(size int) (*MemAlloc, error) {
|
||||
// TODO(maruel): Implement the "shotgun approach". Allocate a ton of 4Kb
|
||||
// pages and lock them. Then look at their physical pages and only keep the
|
||||
// one useful. Then create a linear mapping in memory to simplify the user
|
||||
// mode with a single linear user space virtual address but keep the
|
||||
// individual page alive with their initial allocation. When done release
|
||||
// each individual page.
|
||||
if size > pageSize {
|
||||
return nil, wrapf("large allocation is not yet implemented")
|
||||
}
|
||||
// First allocate a chunk of user space memory.
|
||||
b, err := uallocMemLocked(size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pages := make([]uint64, (size+pageSize-1)/pageSize)
|
||||
// Figure out the physical memory addresses.
|
||||
for i := range pages {
|
||||
pages[i], err = virtToPhys(toRaw(b[pageSize*i:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pages[i] == 0 {
|
||||
return nil, wrapf("failed to read page %d", i)
|
||||
}
|
||||
}
|
||||
for i := 1; i < len(pages); i++ {
|
||||
// Fail if the memory is not contiguous.
|
||||
if pages[i] != pages[i-1]+pageSize {
|
||||
return nil, wrapf("failed to allocate %d bytes of continugous physical memory; page %d =0x%x; page %d=0x%x", size, i, pages[i], i-1, pages[i-1])
|
||||
}
|
||||
}
|
||||
|
||||
return &MemAlloc{View{Slice: b, phys: pages[0], orig: b}}, nil
|
||||
}
|
||||
|
||||
// virtToPhys returns the physical memory address backing a virtual
|
||||
// memory address.
|
||||
func virtToPhys(virt uintptr) (uint64, error) {
|
||||
physPage, err := ReadPageMap(virt)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if physPage&(1<<63) == 0 {
|
||||
// If high bit is not set, the page doesn't exist.
|
||||
return 0, wrapf("0x%08x has no physical address", virt)
|
||||
}
|
||||
// Strip flags. See linux documentation on kernel.org for more details.
|
||||
physPage &^= 0x1FF << 55
|
||||
return physPage * pageSize, nil
|
||||
}
|
||||
|
||||
func toRaw(b []byte) uintptr {
|
||||
h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
return h.Data
|
||||
}
|
||||
|
||||
// isWSL returns true if running under Windows Subsystem for Linux.
|
||||
func isWSL() bool {
|
||||
wslOnce.Do(func() {
|
||||
if c, err := ioutil.ReadFile("/proc/sys/kernel/osrelease"); err == nil {
|
||||
isWSLValue = bytes.Contains(c, []byte("Microsoft"))
|
||||
}
|
||||
})
|
||||
return isWSLValue
|
||||
}
|
||||
|
||||
var _ Mem = &MemAlloc{}
|
69
vendor/periph.io/x/host/v3/pmem/doc.go
generated
vendored
Normal file
69
vendor/periph.io/x/host/v3/pmem/doc.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package pmem implements handling of physical memory for user space programs.
|
||||
//
|
||||
// To make things confusing, a modern computer has many view of the memory
|
||||
// (address spaces):
|
||||
//
|
||||
// User
|
||||
//
|
||||
// User mode address space is the virtual address space that an application
|
||||
// runs in. It is generally a tad less than half the addressable memory, so on
|
||||
// a 32 bits system, the addressable range is 1.9Gb. For 64 bits OS, it depends
|
||||
// but it usually at least 3.5Gb. The memory is virtual and can be flushed to
|
||||
// disk in the swap file unless individual pages are locked.
|
||||
//
|
||||
// Kernel
|
||||
//
|
||||
// Kernel address space is the virtual address space the kernel sees. It often
|
||||
// can see the currently active user space program on the current CPU core in
|
||||
// addition to all the memory the kernel sees. The kernel memory pages that are
|
||||
// not mlock()'ed are 'virtual' and can be flushed to disk in the swap file
|
||||
// when there's not enough RAM available. On linux systems, the kernel
|
||||
// addressed memory can be mapped in user space via `/dev/kmem`.
|
||||
//
|
||||
// Physical
|
||||
//
|
||||
// Physical memory address space is the actual address of each page in the DRAM
|
||||
// chip and anything connected to the memory controller. The mapping may be
|
||||
// different depending on what controller looks at the bus, like with IOMMU. So
|
||||
// a peripheral (GPU, DMA controller) may have a different view of the physical
|
||||
// memory than the host CPU. On linux systems, this memory can be mapped in
|
||||
// user space via `/dev/mem`.
|
||||
//
|
||||
// CPU
|
||||
//
|
||||
// The CPU or its subsystems may memory map registers (for example, to control
|
||||
// GPIO pins, clock speed, etc). This is not "real" memory, this is a view of
|
||||
// registers but it still follows "mostly" the same semantic as DRAM backed
|
||||
// physical memory.
|
||||
//
|
||||
// Some CPU memory may have very special semantic where the mere fact of
|
||||
// reading has side effects. For example reading a specific register may
|
||||
// latches another.
|
||||
//
|
||||
// CPU memory accesses are layered with multiple caches, usually named L1, L2
|
||||
// and optionally L3. Some controllers (DMA) can see some cache levels (L2) but
|
||||
// not others (L1) on some CPU architecture (bcm283x). This means that a user
|
||||
// space program writing data to a memory page and immediately asking the DMA
|
||||
// controller to read it may cause stale data to be read!
|
||||
//
|
||||
// Hypervisor
|
||||
//
|
||||
// Hypervisor can change the complete memory mapping as seen by the kernel.
|
||||
// This is outside the scope of this project. :)
|
||||
//
|
||||
// Summary
|
||||
//
|
||||
// In practice, the semantics change between CPU manufacturers (Broadcom vs
|
||||
// Allwinner) and between architectures (ARM vs x86). The most tricky one is to
|
||||
// understand cached memory and how it affects coherence and performance.
|
||||
// Uncached memory is extremely slow so it must only be used when necessary.
|
||||
//
|
||||
// References
|
||||
//
|
||||
// Overview of IOMMU:
|
||||
// https://en.wikipedia.org/wiki/Input-output_memory_management_unit
|
||||
package pmem
|
56
vendor/periph.io/x/host/v3/pmem/mem_linux.go
generated
vendored
Normal file
56
vendor/periph.io/x/host/v3/pmem/mem_linux.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package pmem
|
||||
|
||||
import "syscall"
|
||||
|
||||
const isLinux = true
|
||||
|
||||
func mmap(fd uintptr, offset int64, length int) ([]byte, error) {
|
||||
v, err := syscall.Mmap(int(fd), offset, length, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
|
||||
if err != nil {
|
||||
return nil, wrapf("failed to memory map: %v", err)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func munmap(b []byte) error {
|
||||
if err := syscall.Munmap(b); err != nil {
|
||||
return wrapf("failed to unmap memory: %v", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func mlock(b []byte) error {
|
||||
if err := syscall.Mlock(b); err != nil {
|
||||
return wrapf("failed to lock memory: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func munlock(b []byte) error {
|
||||
if err := syscall.Munlock(b); err != nil {
|
||||
return wrapf("failed to unlock memory: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// uallocMem allocates user space memory.
|
||||
func uallocMem(size int) ([]byte, error) {
|
||||
b, err := syscall.Mmap(
|
||||
0,
|
||||
0,
|
||||
size,
|
||||
syscall.PROT_READ|syscall.PROT_WRITE,
|
||||
syscall.MAP_ANONYMOUS|syscall.MAP_LOCKED|syscall.MAP_NORESERVE|syscall.MAP_SHARED)
|
||||
// syscall.MAP_HUGETLB / MAP_HUGE_2MB
|
||||
// See /sys/kernel/mm/hugepages but both C.H.I.P. running Jessie and Raspbian
|
||||
// Jessie do not expose huge pages. :(
|
||||
if err != nil {
|
||||
return nil, wrapf("allocating %d bytes failed: %v", size, err)
|
||||
}
|
||||
return b, err
|
||||
}
|
30
vendor/periph.io/x/host/v3/pmem/mem_other.go
generated
vendored
Normal file
30
vendor/periph.io/x/host/v3/pmem/mem_other.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !linux
|
||||
|
||||
package pmem
|
||||
|
||||
const isLinux = false
|
||||
|
||||
func mmap(fd uintptr, offset int64, length int) ([]byte, error) {
|
||||
return nil, wrapf("syscall.Mmap() not implemented on this OS")
|
||||
}
|
||||
|
||||
func munmap(b []byte) error {
|
||||
return wrapf("syscall.Munmap() not implemented on this OS")
|
||||
}
|
||||
|
||||
func mlock(b []byte) error {
|
||||
return wrapf("syscall.Mlock() not implemented on this OS")
|
||||
}
|
||||
|
||||
func munlock(b []byte) error {
|
||||
return wrapf("syscall.Munlock() not implemented on this OS")
|
||||
}
|
||||
|
||||
// uallocMem allocates user space memory.
|
||||
func uallocMem(size int) ([]byte, error) {
|
||||
return make([]byte, size), nil
|
||||
}
|
64
vendor/periph.io/x/host/v3/pmem/pagemap.go
generated
vendored
Normal file
64
vendor/periph.io/x/host/v3/pmem/pagemap.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package pmem
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ReadPageMap reads a physical address mapping for a virtual page address from
|
||||
// /proc/self/pagemap.
|
||||
//
|
||||
// It returns the physical address that corresponds to the start of the virtual
|
||||
// page within which the virtual address virtAddr is located.
|
||||
//
|
||||
// The meaning of the return value is documented at
|
||||
// https://www.kernel.org/doc/Documentation/vm/pagemap.txt
|
||||
func ReadPageMap(virtAddr uintptr) (uint64, error) {
|
||||
if !isLinux || isWSL() {
|
||||
return 0, errors.New("pmem: pagemap is not supported on this platform")
|
||||
}
|
||||
return readPageMapLinux(virtAddr)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var (
|
||||
pageMap fileIO
|
||||
pageMapErr error
|
||||
)
|
||||
|
||||
func readPageMapLinux(virtAddr uintptr) (uint64, error) {
|
||||
var b [8]byte
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if pageMap == nil && pageMapErr == nil {
|
||||
// Open /proc/self/pagemap.
|
||||
//
|
||||
// It is a uint64 array where the index represents the virtual 4Kb page
|
||||
// number and the value represents the physical page properties backing
|
||||
// this virtual page.
|
||||
pageMap, pageMapErr = openFile("/proc/self/pagemap", os.O_RDONLY|os.O_SYNC)
|
||||
}
|
||||
if pageMapErr != nil {
|
||||
return 0, pageMapErr
|
||||
}
|
||||
// Convert address to page number, then index in uint64 array.
|
||||
offset := int64(virtAddr / pageSize * 8)
|
||||
if _, err := pageMap.Seek(offset, os.SEEK_SET); err != nil {
|
||||
return 0, fmt.Errorf("pmem: failed to seek at 0x%x for 0x%x: %v", offset, virtAddr, err)
|
||||
}
|
||||
n, err := pageMap.Read(b[:])
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("pmem: failed to read at 0x%x for 0x%x: %v", offset, virtAddr, err)
|
||||
}
|
||||
if n != len(b) {
|
||||
return 0, fmt.Errorf("pmem: failed to read the amount of data %d", len(b))
|
||||
}
|
||||
return binary.LittleEndian.Uint64(b[:]), nil
|
||||
}
|
89
vendor/periph.io/x/host/v3/pmem/smoketest.go
generated
vendored
Normal file
89
vendor/periph.io/x/host/v3/pmem/smoketest.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package pmem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// TestCopy is used by CPU drivers to verify that the DMA engine works
|
||||
// correctly.
|
||||
//
|
||||
// It is not meant to be used by end users.
|
||||
//
|
||||
// TestCopy allocates two buffer via `alloc`, once as the source and one as the
|
||||
// destination. It fills the source with random data and the destination with
|
||||
// 0x11.
|
||||
//
|
||||
// `copyMem` is expected to copy the memory from pSrc to pDst, with an offset
|
||||
// of `hole` and size `size-2*hole`.
|
||||
//
|
||||
// The function `copyMem` being tested is only given the buffer physical
|
||||
// addresses and must copy the data without other help. It is expected to
|
||||
//
|
||||
// This confirm misaligned DMA copying works.
|
||||
// leverage the host's DMA engine.
|
||||
func TestCopy(size, holeSize int, alloc func(size int) (Mem, error), copyMem func(pDst, pSrc uint64) error) error {
|
||||
pSrc, err2 := alloc(size)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
defer pSrc.Close()
|
||||
pDst, err2 := alloc(size)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
defer pDst.Close()
|
||||
dst := pDst.Bytes()
|
||||
for i := range dst {
|
||||
dst[i] = 0x11
|
||||
}
|
||||
src := make([]byte, size)
|
||||
for i := range src {
|
||||
src[i] = byte(rand.Int31())
|
||||
}
|
||||
copy(pSrc.Bytes(), src[:])
|
||||
|
||||
// Run the driver supplied memory copying code.
|
||||
if err := copyMem(pDst.PhysAddr(), pSrc.PhysAddr()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Verifications.
|
||||
for i := 0; i < holeSize; i++ {
|
||||
if dst[i] != 0x11 {
|
||||
return wrapf("DMA corrupted the buffer header: %x", dst[:holeSize])
|
||||
}
|
||||
if dst[size-1-i] != 0x11 {
|
||||
return wrapf("DMA corrupted the buffer footer: %x", dst[size-1-holeSize:])
|
||||
}
|
||||
}
|
||||
|
||||
// Headers and footers were not corupted in the destination. Verify the inner
|
||||
// view that should match.
|
||||
x := src[:size-2*holeSize]
|
||||
y := dst[holeSize : size-holeSize]
|
||||
if !bytes.Equal(x, y) {
|
||||
offset := 0
|
||||
for len(x) != 0 && x[0] == y[0] {
|
||||
x = x[1:]
|
||||
y = y[1:]
|
||||
offset++
|
||||
}
|
||||
for len(x) != 0 && x[len(x)-1] == y[len(y)-1] {
|
||||
x = x[:len(x)-1]
|
||||
y = y[:len(y)-1]
|
||||
}
|
||||
if len(x) > 32 {
|
||||
x = x[:32]
|
||||
}
|
||||
if len(y) > 32 {
|
||||
y = y[:32]
|
||||
}
|
||||
return wrapf("DMA corrupted the buffer at offset %d:\n%x\n%x", offset, x, y)
|
||||
}
|
||||
return nil
|
||||
}
|
285
vendor/periph.io/x/host/v3/pmem/view.go
generated
vendored
Normal file
285
vendor/periph.io/x/host/v3/pmem/view.go
generated
vendored
Normal file
@ -0,0 +1,285 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package pmem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"periph.io/x/host/v3/fs"
|
||||
)
|
||||
|
||||
// Slice can be transparently viewed as []byte, []uint32 or a struct.
|
||||
type Slice []byte
|
||||
|
||||
// Uint32 returns a view of the byte slice as a []uint32.
|
||||
func (s *Slice) Uint32() []uint32 {
|
||||
// It's important to make a copy here.
|
||||
h := new(reflect.SliceHeader)
|
||||
h.Data = (*reflect.SliceHeader)(unsafe.Pointer(s)).Data
|
||||
h.Len = (*reflect.SliceHeader)(unsafe.Pointer(s)).Len / 4
|
||||
h.Cap = (*reflect.SliceHeader)(unsafe.Pointer(s)).Cap / 4
|
||||
return *(*[]uint32)(unsafe.Pointer(h))
|
||||
}
|
||||
|
||||
// Bytes implements Mem.
|
||||
func (s *Slice) Bytes() []byte {
|
||||
return *s
|
||||
}
|
||||
|
||||
// AsPOD implements Mem.
|
||||
func (s *Slice) AsPOD(pp interface{}) error {
|
||||
if pp == nil {
|
||||
return wrapf("require Ptr, got nil")
|
||||
}
|
||||
vpp := reflect.ValueOf(pp)
|
||||
if elemSize, err := isPS(len(*s), vpp); err == nil {
|
||||
p := vpp.Elem()
|
||||
t := p.Type().Elem()
|
||||
if elemSize > len(*s) {
|
||||
return wrapf("can't map slice of struct %s (size %d) on [%d]byte", t, elemSize, len(*s))
|
||||
}
|
||||
nbElems := len(*s) / elemSize
|
||||
// Use casting black magic to set the internal slice headers.
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(p.UnsafeAddr()))
|
||||
hdr.Data = ((*reflect.SliceHeader)(unsafe.Pointer(s))).Data
|
||||
hdr.Len = nbElems
|
||||
hdr.Cap = nbElems
|
||||
return nil
|
||||
}
|
||||
|
||||
size, err := isPP(vpp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p := vpp.Elem()
|
||||
t := p.Type().Elem()
|
||||
if size > len(*s) {
|
||||
return wrapf("can't map struct %s (size %d) on [%d]byte", t, size, len(*s))
|
||||
}
|
||||
// Use casting black magic to read the internal slice headers.
|
||||
dest := unsafe.Pointer(((*reflect.SliceHeader)(unsafe.Pointer(s))).Data)
|
||||
// Use reflection black magic to write to the original pointer.
|
||||
p.Set(reflect.NewAt(t, dest))
|
||||
return nil
|
||||
}
|
||||
|
||||
// View represents a view of physical memory memory mapped into user space.
|
||||
//
|
||||
// It is usually used to map CPU registers into user space, usually I/O
|
||||
// registers and the likes.
|
||||
//
|
||||
// It is not required to call Close(), the kernel will clean up on process
|
||||
// shutdown.
|
||||
type View struct {
|
||||
Slice
|
||||
orig []uint8 // Reference rounded to the lowest 4Kb page containing Slice.
|
||||
phys uint64 // physical address of the base of Slice.
|
||||
}
|
||||
|
||||
// Close unmaps the memory from the user address space.
|
||||
//
|
||||
// This is done naturally by the OS on process teardown (when the process
|
||||
// exits) so this is not a hard requirement to call this function.
|
||||
func (v *View) Close() error {
|
||||
return munmap(v.orig)
|
||||
}
|
||||
|
||||
// PhysAddr implements Mem.
|
||||
func (v *View) PhysAddr() uint64 {
|
||||
return v.phys
|
||||
}
|
||||
|
||||
// MapGPIO returns a CPU specific memory mapping of the CPU I/O registers using
|
||||
// /dev/gpiomem.
|
||||
//
|
||||
// At the moment, /dev/gpiomem is only supported on Raspbian Jessie via a
|
||||
// specific kernel driver.
|
||||
func MapGPIO() (*View, error) {
|
||||
if isLinux {
|
||||
return mapGPIOLinux()
|
||||
}
|
||||
return nil, wrapf("/dev/gpiomem is not supported on this platform")
|
||||
}
|
||||
|
||||
// Map returns a memory mapped view of arbitrary physical memory range using OS
|
||||
// provided functionality.
|
||||
//
|
||||
// Maps size of memory, rounded on a 4kb window.
|
||||
//
|
||||
// This function is dangerous and should be used wisely. It normally requires
|
||||
// super privileges (root). On Linux, it leverages /dev/mem.
|
||||
func Map(base uint64, size int) (*View, error) {
|
||||
if isLinux {
|
||||
return mapLinux(base, size)
|
||||
}
|
||||
return nil, wrapf("physical memory mapping is not supported on this platform")
|
||||
}
|
||||
|
||||
// MapAsPOD is a leaky shorthand of calling Map(base, sizeof(v)) then AsPOD(v).
|
||||
//
|
||||
// There is no way to reclaim the memory map.
|
||||
//
|
||||
// A slice cannot be used, as it does not have inherent size. Use an aray
|
||||
// instead.
|
||||
func MapAsPOD(base uint64, i interface{}) error {
|
||||
// Automatically determine the necessary size. Because of this, slice of
|
||||
// unspecified length cannot be used here.
|
||||
if i == nil {
|
||||
return wrapf("require Ptr, got nil")
|
||||
}
|
||||
v := reflect.ValueOf(i)
|
||||
size, err := isPP(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := Map(base, size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.AsPOD(i)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Keep a cache of open file handles instead of opening and closing repeatedly.
|
||||
var (
|
||||
mu sync.Mutex
|
||||
gpioMemErr error
|
||||
gpioMemView *View
|
||||
devMem fileIO
|
||||
devMemErr error
|
||||
openFile = openFileOrig
|
||||
)
|
||||
|
||||
type fileIO interface {
|
||||
io.Closer
|
||||
io.Seeker
|
||||
io.Reader
|
||||
Fd() uintptr
|
||||
}
|
||||
|
||||
func openFileOrig(path string, flag int) (fileIO, error) {
|
||||
f, err := fs.Open(path, flag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// mapGPIOLinux is purely Raspbian specific.
|
||||
func mapGPIOLinux() (*View, error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if gpioMemView == nil && gpioMemErr == nil {
|
||||
if f, err := openFile("/dev/gpiomem", os.O_RDWR|os.O_SYNC); err == nil {
|
||||
defer f.Close()
|
||||
if i, err2 := mmap(f.Fd(), 0, pageSize); err2 == nil {
|
||||
gpioMemView = &View{Slice: i, orig: i, phys: 0}
|
||||
} else {
|
||||
gpioMemErr = wrapf("failed to memory map in user space GPIO memory: %w", err2)
|
||||
}
|
||||
} else {
|
||||
gpioMemErr = wrapf("failed to open GPIO memory: %w", err)
|
||||
}
|
||||
}
|
||||
return gpioMemView, gpioMemErr
|
||||
}
|
||||
|
||||
// mapLinux leverages /dev/mem to map a view of physical memory.
|
||||
func mapLinux(base uint64, size int) (*View, error) {
|
||||
f, err := openDevMemLinux()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Align base and size at 4Kb.
|
||||
offset := int(base & 0xFFF)
|
||||
i, err := mmap(f.Fd(), int64(base&^0xFFF), (size+offset+0xFFF)&^0xFFF)
|
||||
if err != nil {
|
||||
return nil, wrapf("mapping at 0x%x failed: %w", base, err)
|
||||
}
|
||||
return &View{Slice: i[offset : offset+size], orig: i, phys: base + uint64(offset)}, nil
|
||||
}
|
||||
|
||||
func openDevMemLinux() (fileIO, error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if devMem == nil && devMemErr == nil {
|
||||
if devMem, devMemErr = openFile("/dev/mem", os.O_RDWR|os.O_SYNC); devMemErr != nil {
|
||||
devMemErr = wrapf("failed to open physical memory: %v", devMemErr)
|
||||
}
|
||||
}
|
||||
return devMem, devMemErr
|
||||
}
|
||||
|
||||
func isAcceptableInner(t reflect.Type) error {
|
||||
switch k := t.Kind(); k {
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64:
|
||||
return nil
|
||||
case reflect.Array:
|
||||
return isAcceptableInner(t.Elem())
|
||||
case reflect.Struct:
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if err := isAcceptableInner(t.Field(i).Type); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return wrapf("require Ptr to Ptr to a POD type, got Ptr to Ptr to %s", k)
|
||||
}
|
||||
}
|
||||
|
||||
// isPP makes sure it is a pointer to a nil-pointer to something. It does
|
||||
// sanity checks to reduce likelihood of a panic().
|
||||
func isPP(pp reflect.Value) (int, error) {
|
||||
if k := pp.Kind(); k != reflect.Ptr {
|
||||
return 0, wrapf("require Ptr, got %s of %s", k, pp.Type().Name())
|
||||
}
|
||||
p := pp.Elem()
|
||||
if k := p.Kind(); k != reflect.Ptr {
|
||||
return 0, wrapf("require Ptr to Ptr, got %s", k)
|
||||
}
|
||||
if !p.IsNil() {
|
||||
return 0, wrapf("require Ptr to Ptr to be nil")
|
||||
}
|
||||
// p.Elem() can't be used since it's a nil pointer. Use the type instead.
|
||||
t := p.Type().Elem()
|
||||
if err := isAcceptableInner(t); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(t.Size()), nil
|
||||
}
|
||||
|
||||
// isPS makes sure it is a pointer to a nil-slice of something. It does
|
||||
// sanity checks to reduce likelihood of a panic().
|
||||
func isPS(bufSize int, ps reflect.Value) (int, error) {
|
||||
if k := ps.Kind(); k != reflect.Ptr {
|
||||
return 0, wrapf("require Ptr, got %s of %s", k, ps.Type().Name())
|
||||
}
|
||||
s := ps.Elem()
|
||||
if k := s.Kind(); k != reflect.Slice {
|
||||
return 0, wrapf("require Ptr to Slice, got %s", k)
|
||||
}
|
||||
if !s.IsNil() {
|
||||
return 0, wrapf("require Ptr to Slice to be nil")
|
||||
}
|
||||
// s.Elem() can't be used since it's a nil slice. Use the type instead.
|
||||
t := s.Type().Elem()
|
||||
if err := isAcceptableInner(t); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(t.Size()), nil
|
||||
}
|
||||
|
||||
func wrapf(format string, a ...interface{}) error {
|
||||
return fmt.Errorf("pmem: "+format, a...)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user