├── .travis.yml ├── DTS ├── SPI0_debslave.dts ├── SPI0_slave.dts └── SPI1_slave.dts ├── LICENSE.txt ├── Makefile ├── README.md ├── documentation ├── spi-slave-core └── spi-slave-dev ├── driver ├── spi-mcspi-slave.c ├── spi-slave-core.c ├── spi-slave-core.h ├── spi-slave-debug.c ├── spi-slave-dev.c └── spi-slave-dev.h ├── python_lib ├── py_spislave.c └── setup.py ├── slave_app ├── slave_app.c └── slave_app.py └── wiki ├── 3layer.png ├── McSPI_Technical_Reference_Manual.pdf ├── beagleboard_logo.png ├── documentation.html ├── gsoc-basic-logo.png ├── how_pio_works.png ├── logo-linux.png ├── one_bb.png ├── pio_10MHz_img.png ├── pio_16MHz_img.png ├── pio_25MHz_img.png ├── pio_2MHz_img.png ├── pio_con.png ├── top_img.png └── two_bb.png /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | os: 4 | - linux 5 | 6 | before_install: 7 | 8 | - sudo apt-get update -qq 9 | 10 | - wget -c https://releases.linaro.org/components/toolchain/binaries/5.3-2016.02/arm-linux-gnueabihf/gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf.tar.xz 11 | - tar xf gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf.tar.xz 12 | - export PATH=$PATH:$PWD/gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf/bin/ 13 | - arm-linux-gnueabihf-gcc --version 14 | - sudo apt-get install python3 15 | - sudo apt-get install bc 16 | 17 | before_script: 18 | 19 | - export DST_PROJECT=$PWD 20 | 21 | - export SOURCE_BRANCH="4.4.9" 22 | - export SOURCE_VERSION="ti-r25" 23 | - export SOURCE_REPO="linux-stable-rcn-ee" 24 | - export SOURCE_LOCATION="https://github.com/RobertCNelson" 25 | 26 | - wget "$SOURCE_LOCATION/$SOURCE_REPO/archive/$SOURCE_BRANCH-$SOURCE_VERSION.tar.gz" 27 | - tar -xf $SOURCE_BRANCH-$SOURCE_VERSION.tar.gz 28 | 29 | - export DST_KERNEL=$PWD/$SOURCE_REPO-$SOURCE_BRANCH-$SOURCE_VERSION 30 | 31 | script: 32 | 33 | - export ARCH=arm 34 | - export CROSS_COMPILE=arm-linux-gnueabihf- 35 | 36 | - cd $DST_KERNEL 37 | - make -j3 mrproper ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOCALVERSION=-$SOURCE_VERSION 38 | - wget -c "http://rcn-ee.net/deb/jessie-armhf/v$SOURCE_BRANCH-$SOURCE_VERSION/defconfig" -O .config 39 | - make -j3 modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOCALVERSION=-$SOURCE_VERSION 2>&1 40 | 41 | - cd $DST_PROJECT/ 42 | 43 | - sudo perl $DST_KERNEL/scripts/checkpatch.pl --no-tree -f slave_app/slave_app.c 44 | - sudo perl $DST_KERNEL/scripts/checkpatch.pl --no-tree -f driver/* 45 | - sudo perl $DST_KERNEL/scripts/checkpatch.pl --no-tree -f firmware/pru_spi_slave.c 46 | - sudo perl $DST_KERNEL/scripts/checkpatch.pl --no-tree -f documentation/* 47 | 48 | - make KDIR=$DST_KERNEL ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOCALVERSION=-$SOURCE_VERSION 49 | - sudo wget -c https://raw.githubusercontent.com/RobertCNelson/tools/master/pkgs/dtc.sh 50 | - sudo chmod +x dtc.sh 51 | - sudo ./dtc.sh 52 | 53 | - cd $DST_PROJECT/DTS/ 54 | 55 | - sudo dtc -O dtb -o SPI0_slave-00A0.dtbo -b 0 -@ SPI0_slave.dts 56 | - sudo dtc -O dtb -o SPI1_slave-00A0.dtbo -b 0 -@ SPI1_slave.dts 57 | 58 | - cd $DST_PROJECT/python_lib/ 59 | - sudo python3 setup.py install 60 | 61 | -------------------------------------------------------------------------------- /DTS/SPI0_debslave.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Patryk Mezydlo 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License version 2 as published by 6 | * the Free Software Foundation. 7 | */ 8 | 9 | /dts-v1/; 10 | /plugin/; 11 | 12 | /* SPI0 slave DTS */ 13 | / { 14 | 15 | compatible = "ti,beaglebone", "ti,beaglebone-black"; 16 | 17 | fragment@0 { 18 | target = <&ocp>; 19 | __overlay__ { 20 | #address-cells = <1>; 21 | #size-cells = <1>; 22 | 23 | spislave0: spi_slave_debug { 24 | compatible = "spislave,spi-slave-debug"; 25 | #address-cells = <1>; 26 | #size-cells = <0>; 27 | status = "okay"; 28 | spislave0@0 { 29 | reg = <0>; 30 | compatible = "linux,spislave"; 31 | }; 32 | }; 33 | }; 34 | }; 35 | }; 36 | -------------------------------------------------------------------------------- /DTS/SPI0_slave.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Patryk Mezydlo 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License version 2 as published by 6 | * the Free Software Foundation. 7 | */ 8 | 9 | /dts-v1/; 10 | /plugin/; 11 | 12 | /* SPI0 slave DTS */ 13 | / { 14 | 15 | compatible = "ti,beaglebone", "ti,beaglebone-black"; 16 | 17 | part-number = "spi0pinmux"; 18 | 19 | fragment@0 { 20 | target = <&am33xx_pinmux>; 21 | __overlay__ { 22 | spi0_pins_s0: spi0_pins_s0 { 23 | pinctrl-single,pins = < 24 | 0x150 0x30 /* spi0_sclk, INPUT_PULLUP | MODE0 */ 25 | 0x154 0x30 /* spi0_d0, INPUT_PULLUP | MODE0 */ 26 | 0x158 0x10 /* spi0_d1, OUTPUT_PULLUP | MODE0 */ 27 | 0x15c 0x30 /* spi0_cs0, INPUT_PULLUP | MODE0 */ 28 | >; 29 | }; 30 | }; 31 | }; 32 | 33 | fragment@1 { 34 | target = <&ocp>; 35 | __overlay__ { 36 | #address-cells = <1>; 37 | #size-cells = <1>; 38 | 39 | spislave0: spi_mcspi_slave0@48030000 { 40 | compatible = "ti,omap4-mcspi-slave"; 41 | #address-cells = <1>; 42 | #size-cells = <0>; 43 | reg = <0x48030000 0x400>; 44 | interrupts = <65>; 45 | ti,spi-num-cs = <2>; 46 | ti,hwmods = "spi0"; 47 | dmas = <&edma 16 0 48 | &edma 17 0 49 | &edma 18 0 50 | &edma 19 0>; 51 | dma-names = "tx0", "rx0", "tx1", "rx1"; 52 | status = "okay"; 53 | pinctrl-names = "default"; 54 | pinctrl-0 = <&spi0_pins_s0>; 55 | spislave0@0 { 56 | reg = <0>; 57 | compatible = "linux,spislave"; 58 | }; 59 | }; 60 | }; 61 | }; 62 | }; 63 | -------------------------------------------------------------------------------- /DTS/SPI1_slave.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Patryk Mezydlo 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License version 2 as published by 6 | * the Free Software Foundation. 7 | */ 8 | 9 | /dts-v1/; 10 | /plugin/; 11 | 12 | /* SPI1 slave dts */ 13 | / { 14 | 15 | compatible = "ti,beaglebone", "ti,beaglebone-black"; 16 | 17 | part-number = "spi1pinmux"; 18 | 19 | fragment@0 { 20 | target = <&am33xx_pinmux>; 21 | __overlay__ { 22 | spi1_pins_s0: spi1_pins_s0 { 23 | pinctrl-single,pins = < 24 | 0x190 0x33 /* spi1_sclk, INPUT_PULLUP | MODE7 */ 25 | 0x194 0x33 /* spi1_d0, INPUT_PULLUP | MODE7 */ 26 | 0x198 0x13 /* spi1_d1, OUTPUT_PULLUP | MODE7 */ 27 | 0x19c 0x33 /* spi1_cs0, INPUT_PULLUP | MODE7 */ 28 | >; 29 | }; 30 | }; 31 | }; 32 | 33 | fragment@1 { 34 | target = <&ocp>; 35 | __overlay__ { 36 | #address-cells = <1>; 37 | #size-cells = <1>; 38 | 39 | spislave1: spi_mcspi_slave1@481a0000 { 40 | compatible = "ti,omap4-mcspi-slave"; 41 | #address-cells = <1>; 42 | #size-cells = <0>; 43 | reg = <0x481a0000 0x400>; 44 | interrupts = <125>; 45 | ti,spi-num-cs = <2>; 46 | ti,hwmods = "spi1"; 47 | dmas = <&edma 42 0 48 | &edma 43 0 49 | &edma 44 0 50 | &edma 45 0>; 51 | dma-names = "tx0", "rx0", "tx1", "rx1"; 52 | status = "okay"; 53 | pinctrl-names = "default"; 54 | pinctrl-0 = <&spi1_pins_s0>; 55 | spislave1@1 { 56 | reg = <0>; 57 | compatible = "linux,spislave"; 58 | }; 59 | }; 60 | }; 61 | }; 62 | }; 63 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ARCH := arm 2 | COMPILER := arm-linux-gnueabihf- 3 | COMPILER_V := gcc 4 | PWD := $(shell pwd) 5 | KERN_V=4.4.30 6 | BUILD_V=-ti-r64 7 | SOURCE=linux-stable-rcn-ee 8 | SUBDIRS=firmware/ 9 | 10 | KDIR := $(PWD)/$(SOURCE)-$(KERN_V)$(BUILD_V) 11 | 12 | obj-m+= driver/spi-slave-debug.o driver/spi-slave-core.o driver/spi-slave-dev.o driver/spi-mcspi-slave.o 13 | 14 | all: 15 | $(MAKE) -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) modules 16 | $(COMPILER)$(COMPILER_V) -o slave_app/slave_app slave_app/slave_app.c 17 | 18 | clean: 19 | $(MAKE) -C $(KDIR) M=$(PWD) ARCH=$(ARCH) clean 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Travis 2 | 3 |

SPI slave driver implementation

4 | This repository contains the code for SPI slave driver realization of my project for GSOC-2016. 5 | 6 |

Introduction

7 | SPI slave driver implementation. The task is to create a driver controlling 8 | SPI hardware controller in slave mode, and to ensure optimal performance through 9 | the use of DMA and interrupt. Creating an easy to implement realization of SPI slave 10 | would definitely help the BeagleBone community members to write applications 11 | based on SPI much more easily. The first implementation of my protocol driver is going 12 | to example of a bidirectional data exchange. This application will provide 13 | the BeagleBone community with valuable experience and will be a good example of SPI slave. 14 | Hardware limitations make it impossible to perform any realization of the device using SPI slave. 15 | Sending away data to the master during one transaction is not possible. One transaction is enough 16 | to receive data by slave device. To receive and send back data, two transactions are needed. 17 | The data received from master device in a single transaction is transferred to the user after 18 | completing the transaction. The user's reply to received data is sent in the next transaction. 19 | 20 | More information is here: 21 | https://github.com/pmezydlo/SPI_slave_driver_implementation/wiki 22 | 23 |

License

24 | SPI slave driver is released under the GPLv2 license. 25 | 26 |

Images

27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /documentation/spi-slave-core: -------------------------------------------------------------------------------- 1 | Overview of Linux kernel SPI Slave support 2 | ========================================== 3 | -------------------------------------------------------------------------------- /documentation/spi-slave-dev: -------------------------------------------------------------------------------- 1 | Overview of SPI slave userspace API for Linux 2 | ============================================= 3 | Very often SPI slave device are cntrolled by a kernel driver, but thanks to 4 | this API, User can use it. Half-duplex and full duplex they are supported. 5 | 6 | Some reasons you might want to use this API include: 7 | 8 | * Simple protocols used to talk to microcontrollers acting as SPI slaves 9 | or SPI masters. 10 | 11 | * Support for master and slave mode. 12 | 13 | * Very easy to implement 14 | 15 | * Support for Python application 16 | 17 | Device can by used by one user at the same time. 18 | 19 | Device creation & Driver binding 20 | ================================ 21 | The simplest way to use this api is to add entry in DTB files: 22 | 23 | spislave0@0 { 24 | reg = <0>; 25 | compatible = "linux, spislave"; 26 | }; 27 | 28 | You have to add this entry as child-node. The child-node name is also the name 29 | for device. In this example the name is "spislave0". 30 | 31 | Implementation details & C and Python examples 32 | =================================== 33 | See the spislave_app.c sample program for one full-duplex transfer. 34 | 35 | So if you want to access an SPI device from a C program , first steps to do 36 | this is add "#include ". Now, you have to check which 37 | spi slave device you want to access. You should inspect /dev/ directory. You 38 | do it by "ls /dev/spislave*". This command showing Spi slave device. 39 | 40 | From Python script, first steps to do this is import SPIslave module. This 41 | line will do this: "import SPIslave" 42 | 43 | When you know which spi slave device you will use you can open it. Normal 44 | open() and close() operations on /dev/spislave* files work as you would 45 | expect. Opening the file looks like this: 46 | 47 | C example: 48 | int file; 49 | char filename[20]; 50 | int spislave_nr = 0; /*your spi slave number*/ 51 | 52 | sprintf(filename, 19, "/dev/spislave%d", spislave_nr); 53 | file = open(filename, O_RDWR); 54 | if (file < 0) { 55 | exit(1); /*make sure to check*/ 56 | } 57 | 58 | Python example: 59 | import SPIslave 60 | spislave_nr = 1 61 | 62 | spi = SPIslave.SPIslave(spislave_nr) 63 | 64 | When you have opened the device, spi slave device allocates memory for your 65 | transfer. Each transfer has settings. Several ioctl() request allow to set 66 | up your transfer: 67 | 68 | SPISLAVE_RD_MODE, SPISLAVE_WR_MODE 69 | Defines basic setting of SPI transfer. 70 | 71 | | BIT(S) | FIELD NAME | DESCRIPTION | 72 | +---------+------------+----------------------------------------------+ 73 | | 7 | TM |Thatt is TM=1 means working in transmit mode. | 74 | +---------+------------+----------------------------------------------+ 75 | | 6 | RM |If this bit is ones, than controler works | 76 | | | |in receive mode. | 77 | +---------+------------+----------------------------------------------+ 78 | | 5 | LSB_FIRST |The bit justification used to transfer spi | 79 | | | |words. Zero indicates MSB-first; other values | 80 | | | |indicate the less common LSB-first encoding. | 81 | | | |MSB-first is set by default. | 82 | +---------+------------+----------------------------------------------- 83 | | 4 | CS_HIGH |That is CS_HIGH means when the CS line state | 84 | | | |is high actives the receiving device. | 85 | +---------+------------+----------------------------------------------+ 86 | | 3 | NO_CS |If NO_CS is zero, that CS is active, | 87 | | | |when NO_CS is set, CS line is not active. | 88 | +---------+------------+----------------------------------------------+ 89 | | 2 | CPOL |If CPOL is zero, than SCLK is normally low, | 90 | | | |and the first clock edge is a rising edge. | 91 | | | |If CPOL is one, SCLK is normally high, and the| 92 | | | |first clock edge is a falling edge. | 93 | +---------+------------+----------------------------------------------+ 94 | | 1 | CPHA |That is CPHA=0 means sampling on the first | 95 | | | |clock edge, while CPHA=1 means sampling on the| 96 | | | |second clock edge, regardless of whether that | 97 | | | |clock edge is rising or falling. | 98 | +---------+------------+----------------------------------------------+ 99 | | 0 | SLAVE |Spi slave subsystem allows to work on both | 100 | | | |master or slave mode. You need to state which | 101 | | | |mode you want to use before transfer. | 102 | | | |Master mode is the main mode and is set by | 103 | | | |default. | 104 | +---------+------------+----------------------------------------------+ 105 | 106 | SPISLAVE_RD_BITS_PER_WORD, SPISLAVE_WR_BITS_PER_WORD 107 | Indicates the number of bits per one word. The typical numer of bits 108 | is 8, 16, 32. The default value is 8 bits. 109 | 110 | SPISLAVE_RD_TX_ACTUAL_LENGTH, SPISLAVE_RD_RX_ACTUAL_LENGTH 111 | When you want to monitore how much bytes is in tx and rx buffer. 112 | You have to use it. 113 | 114 | SPISLAVE_RD_MAX_SPEED, SPISLAVE_WR_MAX_SPEED 115 | Only in master mode. The maximum SPI transfer speed in Hz. The spi 116 | controller does not have to set this setting. Not every controller 117 | supports it. 118 | 119 | Simple example how to use ioctl when you have put setting: 120 | 121 | C example: 122 | int ret; 123 | unsigned int mode = 0; 124 | 125 | mode |= SPISLAVE_CPOL | SPISLAVE_CPHA | SPISLAVE_NO_CS; 126 | 127 | ret = ioctl(file, SPISLAVE_WR_MODE, &mode); 128 | if (ret < 1) { 129 | exit(1); 130 | } 131 | 132 | Python example: 133 | spislave.CPHA = 1 134 | spislave.CPOL = 1 135 | spislave.max_speed = 5000 136 | 137 | 138 | When you have get setting: 139 | 140 | C example: 141 | int ret; 142 | unsigned int max_speed = 0; 143 | 144 | ret = ioctl(file, SPISLAVE_RD_MAX_SPEED, &max_speed); 145 | if (ret < 1) { 146 | exit(1); 147 | } 148 | 149 | Python example: 150 | max_speed = spislave.max_speed 151 | cpol = spislave.cpol 152 | 153 | All setting which isn't default you have to put before transfer. In master 154 | mode the transfer starts when you put message. This is should be done in such 155 | a way: 156 | 157 | C example: 158 | int ret; 159 | char tx[] = {'S', 'P', 'I', ' ', 't', 'e', 's', 't'}; 160 | int size = 8; 161 | 162 | ret = write(file, tx, size); 163 | if (ret < 0) { 164 | perror("Write message"); 165 | exit(1); 166 | } 167 | 168 | Python example: 169 | spislave.write([0xDE, 0xAD, 0xBE, 0xEF]) 170 | 171 | Congratulations! Your message has been sent but this is not the end. Now I will 172 | show you how to receive a message. if you don't know how long is your message 173 | you can check it. Just use ioctl: 174 | 175 | C example: 176 | int ret; 177 | unsigned int size; 178 | 179 | ret = ioctl(file, SPISLAVE_RD_RX_ACTUAL_LENGTH, &size); 180 | if (ret < 1) { 181 | exit(1); 182 | } 183 | 184 | Python example: 185 | size = spislave.rx_actual_length 186 | 187 | Now you know how long is message. Next step is to read message, The reading is 188 | blocked until the end of the transfer. You can call read() functions and when 189 | the transfer ends message will be given to you: 190 | 191 | C example: 192 | char rx[size]; 193 | 194 | ret = read(file, rx, size); 195 | if (ret < 0) { 196 | perror("Read message"); 197 | exit(1); 198 | } 199 | 200 | Python example: 201 | resp = spislave.read() 202 | print resp; 203 | 204 | Finally you have to close file. After this operation all buffers will be 205 | released: 206 | 207 | C example: 208 | close(file); 209 | 210 | Python example: 211 | spislave.close() 212 | -------------------------------------------------------------------------------- /driver/spi-mcspi-slave.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Driver for OMAP2 McSPI controller in slave mode. 3 | * 4 | * Copyright (C) 2016 Patryk Mężydło 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define DRIVER_NAME "spi-mcspi-slave" 25 | 26 | #include 27 | 28 | #include "spi-slave-core.h" 29 | 30 | #define MCSPI_PIN_DIR_D0_IN_D1_OUT 0 31 | #define MCSPI_PIN_DIR_D0_OUT_D1_IN 1 32 | #define MCSPI_CS_POLARITY_ACTIVE_HIGH 1 33 | #define MCSPI_CS_POLARITY_ACTIVE_LOW 0 34 | #define MCSPI_CS_SENSITIVE_ENABLED 1 35 | #define MCSPI_CS_SENSITIVE_DISABLED 0 36 | #define MCSPI_MAX_FIFO_DEPTH 64 37 | #define MCSPI_POL_HELD_HIGH 0 38 | #define MCSPI_POL_HELD_LOW 1 39 | #define MCSPI_PHA_ODD_NUMBERED_EDGES 0 40 | #define MCSPI_PHA_EVEN_NUMBERED_EDGES 1 41 | #define MCSPI_WORDS_PER_LOAD 1 42 | 43 | #define SPI_SLAVE_CS_SENSITIVE MCSPI_CS_SENSITIVE_ENABLED 44 | #define SPI_SLAVE_CS_POLARITY MCSPI_CS_POLARITY_ACTIVE_LOW 45 | #define SPI_SLAVE_PIN_DIR MCSPI_PIN_DIR_D0_IN_D1_OUT 46 | 47 | #define MCSPI_SYSCONFIG 0x10 48 | #define MCSPI_SYSSTATUS 0x14 49 | #define MCSPI_IRQSTATUS 0x18 50 | #define MCSPI_IRQENABLE 0x1C 51 | #define MCSPI_SYST 0x24 52 | #define MCSPI_MODULCTRL 0x28 53 | #define MCSPI_CH0CONF 0x2C 54 | #define MCSPI_CH0STAT 0x30 55 | #define MCSPI_CH0CTRL 0x34 56 | #define MCSPI_TX0 0x38 57 | #define MCSPI_RX0 0x3C 58 | #define MCSPI_XFERLEVEL 0x7C 59 | #define MCSPI_DAFTX 0x80 60 | #define MCSPI_DAFRX 0xA0 61 | 62 | #define SPI_AUTOSUSPEND_TIMEOUT -1 63 | 64 | #define MCSPI_SYSSTATUS_RESETDONE BIT(0) 65 | #define MCSPI_MODULCTRL_MS BIT(2) 66 | #define MCSPI_MODULCTRL_PIN34 BIT(1) 67 | #define MCSPI_CHCTRL_EN BIT(0) 68 | #define MCSPI_CHCONF_EPOL BIT(6) 69 | 70 | #define MCSPI_CHCONF_TRM (0x03 << 12) 71 | #define MCSPI_CHCONF_TM BIT(13) 72 | #define MCSPI_CHCONF_RM BIT(12) 73 | 74 | #define MCSPI_CHCONF_WL (0x1F << 7) 75 | 76 | #define MCSPI_CHCONF_WL_8BIT_MASK (0x07 << 7) 77 | #define MCSPI_CHCONF_WL_16BIT_MASK (0x0F << 7) 78 | #define MCSPI_CHCONF_WL_32BIT_MASK (0x1F << 7) 79 | 80 | #define MCSPI_CHCONF_IS BIT(18) 81 | #define MCSPI_CHCONF_DPE0 BIT(16) 82 | #define MCSPI_CHCONF_DPE1 BIT(17) 83 | #define MCSPI_CHCONF_POL BIT(1) 84 | #define MCSPI_CHCONF_PHA BIT(0) 85 | 86 | #define MCSPI_IRQ_RX_OVERFLOW BIT(3) 87 | #define MCSPI_IRQ_RX_FULL BIT(2) 88 | #define MCSPI_IRQ_TX_UNDERFLOW BIT(1) 89 | #define MCSPI_IRQ_TX_EMPTY BIT(0) 90 | #define MCSPI_IRQ_EOW BIT(17) 91 | 92 | #define MCSPI_SYSCONFIG_CLOCKACTIVITY (0x03 << 8) 93 | #define MCSPI_SYSCONFIG_SIDLEMODE (0x03 << 3) 94 | #define MCSPI_SYSCONFIG_SOFTRESET BIT(1) 95 | #define MCSPI_SYSCONFIG_AUTOIDLE BIT(0) 96 | 97 | #define MCSPI_IRQ_RESET 0xFFFFFFFF 98 | 99 | #define MCSPI_XFER_AFL (0x7 << 8) 100 | #define MCSPI_XFER_AEL (0x7) 101 | #define MCSPI_XFER_WCNT (0xFFFF << 16) 102 | 103 | #define MCSPI_CHCONF_FFER BIT(28) 104 | #define MCSPI_CHCONF_FFEW BIT(27) 105 | 106 | #define MCSPI_MODULCTRL_MOA BIT(7) 107 | #define MCSPI_MODULCTRL_FDAA BIT(8) 108 | 109 | #define MCSPI_CHSTAT_EOT BIT(2) 110 | #define MCSPI_CHSTAT_TXS BIT(1) 111 | #define MCSPI_CHSTAT_RXS BIT(1) 112 | #define MCSPI_CHSTAT_RXFFF BIT(6) 113 | #define MCSPI_CHSTAT_RXFFE BIT(5) 114 | #define MCSPI_CHSTAT_TXFFF BIT(4) 115 | #define MCSPI_CHSTAT_TXFFE BIT(3) 116 | 117 | struct mcspi_drv { 118 | void __iomem *base; 119 | unsigned int pin_dir; 120 | u32 cs_sensitive; 121 | u32 cs_polarity; 122 | unsigned int pha; 123 | unsigned int pol; 124 | unsigned int irq; 125 | }; 126 | 127 | inline unsigned int mcspi_slave_read_reg(void __iomem *base, u32 idx) 128 | { 129 | return ioread32(base + idx); 130 | } 131 | 132 | inline void mcspi_slave_write_reg(void __iomem *base, 133 | u32 idx, u32 val) 134 | { 135 | iowrite32(val, base + idx); 136 | } 137 | 138 | int mcspi_slave_bytes_per_word(int word_len) 139 | { 140 | if (word_len <= 8) 141 | return 1; 142 | else if (word_len <= 16) 143 | return 2; 144 | else 145 | return 4; 146 | } 147 | 148 | int mcspi_slave_wait_for_bit(void __iomem *reg, u32 bit) 149 | { 150 | unsigned long timeout; 151 | 152 | timeout = jiffies + msecs_to_jiffies(1000); 153 | while (!(ioread32(reg) & bit)) { 154 | if (time_after(jiffies, timeout)) 155 | return -ETIMEDOUT; 156 | else 157 | return 0; 158 | 159 | cpu_relax(); 160 | } 161 | return 0; 162 | } 163 | 164 | void mcspi_slave_enable(struct mcspi_drv *mcspi) 165 | { 166 | u32 val; 167 | 168 | pr_info("%s: function: enable\n", DRIVER_NAME); 169 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_CH0CTRL); 170 | val |= MCSPI_CHCTRL_EN; 171 | mcspi_slave_write_reg(mcspi->base, MCSPI_CH0CTRL, val); 172 | } 173 | 174 | void mcspi_slave_disable(struct mcspi_drv *mcspi) 175 | { 176 | u32 val; 177 | 178 | pr_info("%s: function: disable\n", DRIVER_NAME); 179 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_CH0CTRL); 180 | val &= ~MCSPI_CHCTRL_EN; 181 | mcspi_slave_write_reg(mcspi->base, MCSPI_CH0CTRL, val); 182 | } 183 | 184 | void mcspi_slave_pio_rx_transfer(unsigned long data) 185 | { 186 | struct spislave *slave = (struct spislave *)data; 187 | struct mcspi_drv *mcspi = (struct mcspi_drv *)slave->spislave_gadget; 188 | struct spislave_message *msg = slave->msg; 189 | unsigned int word_counter; 190 | void __iomem *rx_reg; 191 | void __iomem *chstat; 192 | 193 | pr_info("%s: funtion: pio_rx_transfer\n", DRIVER_NAME); 194 | 195 | rx_reg = mcspi->base + MCSPI_RX0; 196 | chstat = mcspi->base + MCSPI_CH0STAT; 197 | 198 | word_counter = MCSPI_WORDS_PER_LOAD; 199 | word_counter /= mcspi_slave_bytes_per_word(msg->bits_per_word); 200 | 201 | if (msg->rx_actual_length >= msg->buf_depth) { 202 | dev_dbg(&slave->dev, "end of rx buffer!\n"); 203 | msg->rx_actual_length = 0; 204 | return; 205 | } 206 | 207 | switch (mcspi_slave_bytes_per_word(msg->bits_per_word)) { 208 | case 1: { 209 | u8 *rx; 210 | 211 | rx = msg->rx + msg->rx_actual_length; 212 | msg->rx_actual_length += (sizeof(u8) * word_counter); 213 | 214 | do { 215 | word_counter -= 1; 216 | if (mcspi_slave_wait_for_bit(chstat, MCSPI_CHSTAT_RXS) 217 | < 0) 218 | goto out; 219 | 220 | pr_info("%s: rx:0x%x\n", DRIVER_NAME, *rx); 221 | *rx++ = readl_relaxed(rx_reg); 222 | } while (word_counter); 223 | } break; 224 | 225 | case 2: { 226 | u16 *rx; 227 | 228 | rx = msg->rx + msg->rx_actual_length; 229 | msg->rx_actual_length += (sizeof(u16) * word_counter); 230 | 231 | do { 232 | word_counter -= 1; 233 | if (mcspi_slave_wait_for_bit(chstat, MCSPI_CHSTAT_RXS) 234 | < 0) 235 | goto out; 236 | 237 | *rx++ = readl_relaxed(rx_reg); 238 | } while (word_counter); 239 | } break; 240 | 241 | case 4: { 242 | u32 *rx; 243 | 244 | rx = msg->rx + msg->rx_actual_length; 245 | msg->rx_actual_length += (sizeof(u32) * word_counter); 246 | 247 | do { 248 | word_counter -= 1; 249 | if (mcspi_slave_wait_for_bit(chstat, MCSPI_CHSTAT_RXS) 250 | < 0) 251 | goto out; 252 | 253 | *rx++ = readl_relaxed(rx_reg); 254 | } while (word_counter); 255 | } break; 256 | 257 | default: 258 | return; 259 | } 260 | 261 | return; 262 | out: 263 | dev_dbg(&slave->dev, "timeout!\n"); 264 | } 265 | DECLARE_TASKLET(pio_rx_tasklet, mcspi_slave_pio_rx_transfer, 0); 266 | 267 | int mcspi_slave_pio_tx_transfer(struct spislave *slave) 268 | { 269 | struct mcspi_drv *mcspi = (struct mcspi_drv *)slave->spislave_gadget; 270 | struct spislave_message *msg = slave->msg; 271 | unsigned int word_counter; 272 | void __iomem *tx_reg; 273 | void __iomem *chstat; 274 | 275 | pr_info("%s: function: pio_tx_transfer\n", DRIVER_NAME); 276 | 277 | tx_reg = mcspi->base + MCSPI_TX0; 278 | chstat = mcspi->base + MCSPI_CH0STAT; 279 | word_counter = MCSPI_MAX_FIFO_DEPTH / 2; 280 | 281 | word_counter /= mcspi_slave_bytes_per_word(msg->bits_per_word); 282 | 283 | if (msg->tx_actual_length >= msg->buf_depth) { 284 | dev_dbg(&slave->dev, "end of tx buffer!\n"); 285 | msg->tx_actual_length = 0; 286 | return -EMSGSIZE; 287 | } 288 | 289 | switch (mcspi_slave_bytes_per_word(msg->bits_per_word)) { 290 | case 1: { 291 | const u8 *tx; 292 | 293 | tx = msg->tx + msg->tx_actual_length; 294 | msg->tx_actual_length += (sizeof(u8) * word_counter); 295 | 296 | do { 297 | word_counter -= 1; 298 | if (mcspi_slave_wait_for_bit(chstat, MCSPI_CHSTAT_TXS) 299 | < 0) 300 | goto out; 301 | 302 | writel_relaxed(*tx++, tx_reg); 303 | pr_info("%s: tx:0x%x\n", DRIVER_NAME, *tx); 304 | } while (word_counter); 305 | } break; 306 | case 2: { 307 | const u16 *tx; 308 | 309 | tx = msg->tx + msg->tx_actual_length; 310 | msg->tx_actual_length += (sizeof(u16) * word_counter); 311 | 312 | do { 313 | word_counter -= 1; 314 | if (mcspi_slave_wait_for_bit(chstat, MCSPI_CHSTAT_TXS) 315 | < 0) 316 | goto out; 317 | 318 | writel_relaxed(*tx++, tx_reg); 319 | } while (word_counter); 320 | } break; 321 | case 4: { 322 | const u32 *tx; 323 | 324 | tx = msg->tx + msg->tx_actual_length; 325 | msg->tx_actual_length += (sizeof(u32) * word_counter); 326 | 327 | do { 328 | word_counter -= 1; 329 | if (mcspi_slave_wait_for_bit(chstat, MCSPI_CHSTAT_TXS) 330 | < 0) 331 | goto out; 332 | 333 | writel_relaxed(*tx++, tx_reg); 334 | } while (word_counter); 335 | } break; 336 | default: 337 | return -EIO; 338 | } 339 | 340 | return 0; 341 | out: 342 | dev_dbg(&slave->dev, "timeout!!!\n"); 343 | return -EIO; 344 | } 345 | 346 | irq_handler_t mcspi_slave_irq(unsigned int irq, void *dev_id) 347 | { 348 | struct spislave *slave = (struct spislave *)dev_id; 349 | struct mcspi_drv *mcspi = (struct mcspi_drv *)slave->spislave_gadget; 350 | struct spislave_message *msg = slave->msg; 351 | u32 val; 352 | unsigned long flags; 353 | 354 | pr_info("%s: function: irq\n", DRIVER_NAME); 355 | 356 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_CH0STAT); 357 | 358 | if (val & MCSPI_CHSTAT_EOT) { 359 | spin_lock_irqsave(&msg->wait_lock, flags); 360 | wake_up_all(&msg->wait); 361 | spin_unlock_irqrestore(&msg->wait_lock, flags); 362 | mcspi_slave_disable(mcspi); 363 | pr_info("%s: irq: EOT is set\n", DRIVER_NAME); 364 | } 365 | 366 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_IRQSTATUS); 367 | 368 | if (val & MCSPI_IRQ_RX_FULL) { 369 | val |= MCSPI_IRQ_RX_FULL; 370 | pio_rx_tasklet.data = (unsigned long)slave; 371 | tasklet_schedule(&pio_rx_tasklet); 372 | pr_info("%s: irq: rx is full\n", DRIVER_NAME); 373 | } 374 | 375 | mcspi_slave_write_reg(mcspi->base, MCSPI_IRQSTATUS, val); 376 | 377 | return (irq_handler_t) IRQ_HANDLED; 378 | } 379 | 380 | int mcspi_slave_set_irq(struct spislave *slave) 381 | { 382 | struct mcspi_drv *mcspi = (struct mcspi_drv *)slave->spislave_gadget; 383 | u32 val; 384 | int ret; 385 | 386 | pr_info("%s: function: set irq\n", DRIVER_NAME); 387 | 388 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_IRQENABLE); 389 | val &= ~MCSPI_IRQ_RX_FULL; 390 | val &= ~MCSPI_IRQ_TX_EMPTY; 391 | val |= MCSPI_IRQ_RX_FULL; 392 | mcspi_slave_write_reg(mcspi->base, MCSPI_IRQENABLE, val); 393 | 394 | ret = devm_request_irq(&slave->dev, mcspi->irq, 395 | (irq_handler_t)mcspi_slave_irq, 396 | IRQF_TRIGGER_NONE, 397 | DRIVER_NAME, slave); 398 | pr_info("%s: irq ret:%d\n", DRIVER_NAME, ret); 399 | 400 | if (ret) { 401 | dev_dbg(&slave->dev, "unable to request irq:%d\n", mcspi->irq); 402 | return -EINTR; 403 | } 404 | 405 | return 0; 406 | } 407 | 408 | void mcspi_slave_setup_pio_trnasfer(struct spislave *slave) 409 | { 410 | struct mcspi_drv *mcspi = (struct mcspi_drv *)slave->spislave_gadget; 411 | struct spislave_message *msg = slave->msg; 412 | u32 val; 413 | 414 | pr_info("%s: function: setup pio transfer\n", DRIVER_NAME); 415 | 416 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_XFERLEVEL); 417 | val &= ~MCSPI_XFER_AEL; 418 | val &= ~MCSPI_XFER_AFL; 419 | val |= (MCSPI_WORDS_PER_LOAD * 420 | mcspi_slave_bytes_per_word(msg->bits_per_word)) << 8; 421 | val &= ~MCSPI_XFER_WCNT; 422 | pr_info("%s: setup pio: XFERLEVEL:%x\n", DRIVER_NAME, val); 423 | mcspi_slave_write_reg(mcspi->base, MCSPI_XFERLEVEL, val); 424 | 425 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_CH0CONF); 426 | val &= ~MCSPI_CHCONF_TRM; 427 | val &= ~MCSPI_CHCONF_WL; 428 | val |= (msg->bits_per_word - 1) << 7; 429 | val &= ~MCSPI_CHCONF_FFER; 430 | val &= ~MCSPI_CHCONF_FFEW; 431 | pr_info("%s: setup pio: CH0DONF:%x\n", DRIVER_NAME, val); 432 | mcspi_slave_write_reg(mcspi->base, MCSPI_CH0CONF, val); 433 | 434 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_MODULCTRL); 435 | val &= ~MCSPI_MODULCTRL_FDAA; 436 | pr_info("%s: setup pio: MODULCTRL:%x\n", DRIVER_NAME, val); 437 | mcspi_slave_write_reg(mcspi->base, MCSPI_MODULCTRL, val); 438 | } 439 | 440 | void mcspi_slave_set_mode(struct mcspi_drv *mcspi) 441 | { 442 | u32 val; 443 | 444 | pr_info("%s: function: set mode\n", DRIVER_NAME); 445 | 446 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_MODULCTRL); 447 | val |= MCSPI_MODULCTRL_MS; 448 | pr_info("%s: set mode: MODULCTRL:%x\n", DRIVER_NAME, val); 449 | mcspi_slave_write_reg(mcspi->base, MCSPI_MODULCTRL, val); 450 | 451 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_CH0CONF); 452 | val &= ~MCSPI_CHCONF_PHA; 453 | val &= ~MCSPI_CHCONF_POL; 454 | 455 | if (mcspi->pin_dir == MCSPI_PIN_DIR_D0_IN_D1_OUT) { 456 | val &= ~MCSPI_CHCONF_IS; 457 | val &= ~MCSPI_CHCONF_DPE1; 458 | val |= MCSPI_CHCONF_DPE0; 459 | } else { 460 | val |= MCSPI_CHCONF_IS; 461 | val |= MCSPI_CHCONF_DPE1; 462 | val &= ~MCSPI_CHCONF_DPE0; 463 | } 464 | 465 | if (mcspi->pol == MCSPI_POL_HELD_HIGH) 466 | val &= ~MCSPI_CHCONF_POL; 467 | else 468 | val |= MCSPI_CHCONF_POL; 469 | 470 | if (mcspi->pha == MCSPI_PHA_ODD_NUMBERED_EDGES) 471 | val &= ~MCSPI_CHCONF_PHA; 472 | else 473 | val |= MCSPI_CHCONF_PHA; 474 | 475 | pr_info("%s: setmode: val:%x\n", DRIVER_NAME, val); 476 | mcspi_slave_write_reg(mcspi->base, MCSPI_CH0CONF, val); 477 | } 478 | 479 | void mcspi_slave_set_cs(struct mcspi_drv *mcspi) 480 | { 481 | u32 val; 482 | 483 | pr_info("%s: function: set cs\n", DRIVER_NAME); 484 | 485 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_CH0CONF); 486 | 487 | if (mcspi->cs_polarity == MCSPI_CS_POLARITY_ACTIVE_LOW) 488 | val |= MCSPI_CHCONF_EPOL; 489 | else 490 | val &= ~MCSPI_CHCONF_EPOL; 491 | 492 | pr_info("%s: set cs:%x\n", DRIVER_NAME, val); 493 | mcspi_slave_write_reg(mcspi->base, MCSPI_CH0CONF, val); 494 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_MODULCTRL); 495 | 496 | if (mcspi->cs_sensitive == MCSPI_CS_SENSITIVE_ENABLED) 497 | val &= ~MCSPI_MODULCTRL_PIN34; 498 | else 499 | val |= MCSPI_MODULCTRL_PIN34; 500 | 501 | pr_info("%s: set cs:%x\n", DRIVER_NAME, val); 502 | mcspi_slave_write_reg(mcspi->base, MCSPI_MODULCTRL, val); 503 | } 504 | 505 | int mcspi_slave_setup(struct spislave *slave) 506 | { 507 | struct mcspi_drv *mcspi = (struct mcspi_drv *)slave->spislave_gadget; 508 | int ret; 509 | 510 | pr_info("%s: function: setup\n", DRIVER_NAME); 511 | 512 | if (mcspi_slave_wait_for_bit(mcspi->base + MCSPI_SYSSTATUS, 513 | MCSPI_SYSSTATUS_RESETDONE) != 0) { 514 | dev_dbg(&slave->dev, "internal module reset is on-going\n"); 515 | return -EIO; 516 | } 517 | 518 | mcspi_slave_disable(mcspi); 519 | mcspi_slave_set_mode(mcspi); 520 | mcspi_slave_set_cs(mcspi); 521 | ret = mcspi_slave_set_irq(slave); 522 | 523 | if (ret < 0) 524 | return -EINTR; 525 | 526 | return 0; 527 | } 528 | 529 | int mcspi_slave_transfer(struct spislave *slave) 530 | { 531 | struct mcspi_drv *mcspi = (struct mcspi_drv *)slave->spislave_gadget; 532 | struct spislave_message *msg = slave->msg; 533 | int ret; 534 | 535 | pr_info("%s: function: transfer\n", DRIVER_NAME); 536 | 537 | mcspi_slave_setup_pio_trnasfer(slave); 538 | mcspi_slave_enable(mcspi); 539 | msg->tx_actual_length = 0; 540 | msg->rx_actual_length = 0; 541 | 542 | pr_info("%s: var: mode=%d\n", DRIVER_NAME, msg->mode); 543 | pr_info("%s: var: bits_per_word=%d\n", DRIVER_NAME, msg->bits_per_word); 544 | pr_info("%s: var: buf_depth=%d\n", DRIVER_NAME, msg->buf_depth); 545 | 546 | ret = mcspi_slave_pio_tx_transfer(slave); 547 | if (ret < 0) 548 | return -EFAULT; 549 | 550 | return 0; 551 | } 552 | 553 | void mcspi_slave_clear(struct spislave *slave) 554 | { 555 | struct mcspi_drv *mcspi = (struct mcspi_drv *)slave->spislave_gadget; 556 | u32 val; 557 | 558 | pr_info("%s: function: clear\n", DRIVER_NAME); 559 | 560 | val = mcspi_slave_read_reg(mcspi->base, MCSPI_SYSCONFIG); 561 | val |= MCSPI_SYSCONFIG_SOFTRESET; 562 | mcspi_slave_write_reg(mcspi->base, MCSPI_SYSCONFIG, val); 563 | mcspi_slave_disable(mcspi); 564 | } 565 | 566 | struct omap2_mcspi_platform_config mcspi_slave_pdata = { 567 | .regs_offset = OMAP4_MCSPI_REG_OFFSET, 568 | }; 569 | 570 | const struct of_device_id mcspi_slave_of_match[] = { 571 | { 572 | .compatible = "spislave,spi-slave-debug", 573 | .data = &mcspi_slave_pdata, 574 | }, 575 | { } 576 | }; 577 | MODULE_DEVICE_TABLE(of, mcspi_slave_of_match); 578 | 579 | static int mcspi_slave_probe(struct platform_device *pdev) 580 | { 581 | struct resource *res; 582 | struct resource cp_res; 583 | const struct of_device_id *match; 584 | const struct omap2_mcspi_platform_config *pdata; 585 | struct spislave *slave; 586 | struct mcspi_drv *mcspi; 587 | int ret = 0; 588 | u32 regs_offset = 0; 589 | 590 | u32 cs_sensitive; 591 | u32 cs_polarity; 592 | unsigned int pin_dir; 593 | unsigned int irq; 594 | unsigned int pha; 595 | unsigned int pol; 596 | 597 | pr_info("%s: function: probe\n", DRIVER_NAME); 598 | 599 | slave = spislave_alloc_slave(&pdev->dev, sizeof(struct spislave)); 600 | if (slave == NULL) 601 | return -ENOMEM; 602 | 603 | pr_info("%s: act: alloc slave\n", DRIVER_NAME); 604 | 605 | mcspi = kzalloc(sizeof(*mcspi), GFP_KERNEL); 606 | slave->spislave_gadget = mcspi; 607 | pr_info("%s: act: alloc mcspi strust\n", DRIVER_NAME); 608 | 609 | match = of_match_device(mcspi_slave_of_match, &pdev->dev); 610 | 611 | if (!match) { 612 | dev_dbg(&slave->dev, "failed to match, install DTS\n"); 613 | ret = -EINVAL; 614 | goto free_slave; 615 | } 616 | 617 | pr_info("%s: act: of match device\n", DRIVER_NAME); 618 | 619 | pdata = match->data; 620 | 621 | if (of_get_property(pdev->dev.of_node, "cs_polarity", &cs_polarity)) 622 | cs_polarity = MCSPI_CS_POLARITY_ACTIVE_HIGH; 623 | else 624 | cs_polarity = MCSPI_CS_POLARITY_ACTIVE_LOW; 625 | 626 | if (of_get_property(pdev->dev.of_node, "cs_sensitive", &cs_sensitive)) 627 | cs_sensitive = MCSPI_CS_SENSITIVE_DISABLED; 628 | else 629 | cs_sensitive = MCSPI_CS_SENSITIVE_ENABLED; 630 | 631 | if (of_get_property(pdev->dev.of_node, "pindir-D0-out-D1-in", &pin_dir)) 632 | pin_dir = MCSPI_PIN_DIR_D0_OUT_D1_IN; 633 | else 634 | pin_dir = MCSPI_PIN_DIR_D0_IN_D1_OUT; 635 | 636 | if (of_get_property(pdev->dev.of_node, "pha", &pha)) 637 | pha = MCSPI_PHA_EVEN_NUMBERED_EDGES; 638 | else 639 | pha = MCSPI_PHA_ODD_NUMBERED_EDGES; 640 | 641 | if (of_get_property(pdev->dev.of_node, "pol", &pol)) 642 | pol = MCSPI_POL_HELD_LOW; 643 | else 644 | pol = MCSPI_POL_HELD_HIGH; 645 | 646 | irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 647 | regs_offset = pdata->regs_offset; 648 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 649 | memcpy(&cp_res, res, sizeof(struct resource)); 650 | 651 | pr_info("%s: act: irq, platform get resou\n", DRIVER_NAME); 652 | 653 | if (!res) { 654 | ret = -ENODEV; 655 | goto free_slave; 656 | } 657 | 658 | cp_res.start += regs_offset; 659 | cp_res.end += regs_offset; 660 | 661 | mcspi->base = devm_ioremap_resource(&pdev->dev, &cp_res); 662 | 663 | if (IS_ERR(mcspi->base)) { 664 | ret = PTR_ERR(mcspi->base); 665 | goto free_slave; 666 | } 667 | 668 | pr_info("%s: act: devm ioremap reso\n", DRIVER_NAME); 669 | 670 | mcspi->cs_polarity = cs_polarity; 671 | mcspi->cs_sensitive = cs_sensitive; 672 | mcspi->pin_dir = pin_dir; 673 | mcspi->irq = irq; 674 | mcspi->pol = pol; 675 | mcspi->pha = pha; 676 | 677 | /* FIXME: 678 | * CPOL and CPHA doesnt depend on device property which 679 | * located in dts file but CPOL and CPHA is setting when message is on 680 | * going 681 | */ 682 | 683 | 684 | slave->transfer_msg = mcspi_slave_transfer; 685 | slave->clear_msg = mcspi_slave_clear; 686 | 687 | platform_set_drvdata(pdev, mcspi); 688 | 689 | pr_info("%s: act: platform set drv\n", DRIVER_NAME); 690 | 691 | pm_runtime_use_autosuspend(&pdev->dev); 692 | pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); 693 | pm_runtime_enable(&pdev->dev); 694 | 695 | ret = pm_runtime_get_sync(&pdev->dev); 696 | if (ret < 0) 697 | goto disable_pm; 698 | 699 | pr_info("%s: act: pm runtime\n", DRIVER_NAME); 700 | 701 | ret = mcspi_slave_setup(slave); 702 | if (ret < 0) 703 | goto disable_pm; 704 | 705 | pr_info("%s: act: setup slave\n", DRIVER_NAME); 706 | 707 | ret = devm_spislave_register_slave(&pdev->dev, slave); 708 | if (ret) { 709 | dev_dbg(&slave->dev, "register device error\n"); 710 | goto disable_pm; 711 | } 712 | 713 | pr_info("%s: act: devm spislave register\n", DRIVER_NAME); 714 | 715 | return ret; 716 | 717 | disable_pm: 718 | pm_runtime_dont_use_autosuspend(&pdev->dev); 719 | pm_runtime_put_sync(&pdev->dev); 720 | pm_runtime_disable(&pdev->dev); 721 | 722 | free_slave: 723 | 724 | if (!slave) { 725 | put_device(&slave->dev); 726 | mcspi_slave_clear(slave); 727 | } 728 | 729 | return ret; 730 | } 731 | 732 | static int mcspi_slave_remove(struct platform_device *pdev) 733 | { 734 | pr_info("%s: function: remove\n", DRIVER_NAME); 735 | 736 | tasklet_kill(&pio_rx_tasklet); 737 | 738 | pm_runtime_dont_use_autosuspend(&pdev->dev); 739 | pm_runtime_put_sync(&pdev->dev); 740 | pm_runtime_disable(&pdev->dev); 741 | 742 | return 0; 743 | } 744 | 745 | static struct platform_driver mcspi_slave_driver = { 746 | .probe = mcspi_slave_probe, 747 | .remove = mcspi_slave_remove, 748 | .driver = { 749 | .name = DRIVER_NAME, 750 | .of_match_table = of_match_ptr(mcspi_slave_of_match), 751 | }, 752 | }; 753 | 754 | module_platform_driver(mcspi_slave_driver); 755 | 756 | MODULE_LICENSE("GPL v2"); 757 | MODULE_AUTHOR("Patryk Mezydlo, "); 758 | MODULE_DESCRIPTION("SPI slave for McSPI controller."); 759 | MODULE_VERSION("1.0"); 760 | -------------------------------------------------------------------------------- /driver/spi-slave-core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A device driver for the SPI slave mode bus interface. 3 | * 4 | * Copyright (C) 2016 Patryk Mężydło 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "spi-slave-core.h" 21 | #define DRIVER_NAME "spislavecore" 22 | 23 | /*============================================================================*/ 24 | struct spislave_message *spislave_msg_alloc(struct spislave *slave) 25 | { 26 | struct spislave_message *msg; 27 | 28 | pr_info("%s: function: msg alloc\n", DRIVER_NAME); 29 | 30 | msg = slave->msg; 31 | 32 | msg = kzalloc(sizeof(*msg), GFP_KERNEL); 33 | if (!msg) 34 | return NULL; 35 | 36 | mutex_init(&msg->msg_lock); 37 | spin_lock_init(&msg->wait_lock); 38 | init_waitqueue_head(&msg->wait); 39 | slave->msg = msg; 40 | 41 | return msg; 42 | } 43 | EXPORT_SYMBOL_GPL(spislave_msg_alloc); 44 | 45 | void spislave_msg_remove(struct spislave *slave) 46 | { 47 | struct spislave_message *msg; 48 | 49 | pr_info("%s: function: msg remove\n", DRIVER_NAME); 50 | 51 | if (slave->clear_msg != NULL) 52 | slave->clear_msg(slave); 53 | 54 | msg = slave->msg; 55 | kfree(msg); 56 | } 57 | EXPORT_SYMBOL_GPL(spislave_msg_remove); 58 | 59 | int spislave_transfer_msg(struct spislave *slave) 60 | { 61 | int ret; 62 | 63 | pr_info("%s: function: transfer msg\n", DRIVER_NAME); 64 | 65 | if (slave->transfer_msg != NULL) 66 | ret = slave->transfer_msg(slave); 67 | 68 | return ret; 69 | } 70 | EXPORT_SYMBOL_GPL(spislave_transfer_msg); 71 | 72 | int spislave_clear_transfer(struct spislave *slave) 73 | { 74 | pr_info("%s: function: clear transfer\n", DRIVER_NAME); 75 | 76 | if (slave->clear_msg != NULL) 77 | slave->clear_msg(slave); 78 | 79 | return 0; 80 | } 81 | EXPORT_SYMBOL_GPL(spislave_clear_transfer); 82 | 83 | /*============================================================================*/ 84 | static int spislave_drv_probe(struct device *dev) 85 | { 86 | int ret = 0; 87 | const struct spislave_driver *sdrv; 88 | struct spislave_device *sdev; 89 | 90 | pr_info("%s: probe\n", DRIVER_NAME); 91 | 92 | sdrv = to_spislave_drv(dev->driver); 93 | sdev = to_spislave_dev(dev); 94 | 95 | ret = dev_pm_domain_attach(dev, true); 96 | if (ret != -EPROBE_DEFER) { 97 | ret = sdrv->probe(sdev); 98 | if (ret) 99 | dev_pm_domain_detach(dev, true); 100 | } 101 | 102 | return ret; 103 | } 104 | 105 | static int spislave_drv_remove(struct device *dev) 106 | { 107 | int ret = 0; 108 | const struct spislave_driver *sdrv; 109 | struct spislave_device *sdev; 110 | 111 | pr_info("%s: remove\n", DRIVER_NAME); 112 | 113 | sdrv = to_spislave_drv(dev->driver); 114 | 115 | if (!sdrv) 116 | return -ENODEV; 117 | 118 | sdev = to_spislave_dev(dev); 119 | 120 | if (!sdev) 121 | return -ENODEV; 122 | 123 | ret = sdrv->remove(sdev); 124 | dev_pm_domain_detach(dev, true); 125 | 126 | return ret; 127 | } 128 | 129 | int spislave_register_driver(struct spislave_driver *sdrv) 130 | { 131 | pr_info("%s: register driver\n", DRIVER_NAME); 132 | 133 | sdrv->driver.owner = THIS_MODULE; 134 | sdrv->driver.bus = &spislave_bus_type; 135 | 136 | sdrv->driver.probe = spislave_drv_probe; 137 | sdrv->driver.remove = spislave_drv_remove; 138 | 139 | return driver_register(&sdrv->driver); 140 | } 141 | EXPORT_SYMBOL_GPL(spislave_register_driver); 142 | 143 | void spislave_unregister_driver(struct spislave_driver *sdrv) 144 | { 145 | if (sdrv) 146 | driver_unregister(&sdrv->driver); 147 | } 148 | EXPORT_SYMBOL_GPL(spislave_unregister_driver); 149 | 150 | /*============================================================================*/ 151 | static int __unregister(struct device *dev, void *null) 152 | { 153 | spislave_unregister_device(to_spislave_dev(dev)); 154 | return 0; 155 | } 156 | 157 | void spislave_unregister_slave(struct spislave *slave) 158 | { 159 | int dummy; 160 | 161 | pr_info("%s: function: unregister slave\n", DRIVER_NAME); 162 | 163 | dummy = device_for_each_child(&slave->dev, NULL, __unregister); 164 | device_unregister(&slave->dev); 165 | } 166 | EXPORT_SYMBOL_GPL(spislave_unregister_slave); 167 | 168 | static void devm_spislave_unregister_slave(struct device *dev, void *res) 169 | { 170 | spislave_unregister_slave(*(struct spislave **)res); 171 | } 172 | EXPORT_SYMBOL_GPL(devm_spislave_unregister_slave); 173 | 174 | static void spislave_dev_release(struct device *dev) 175 | { 176 | struct spislave_device *slave_dev = to_spislave_dev(dev); 177 | struct spislave *slave; 178 | 179 | pr_info("%s: function: adev release\n", DRIVER_NAME); 180 | 181 | slave = slave_dev->slave; 182 | 183 | put_device(&slave->dev); 184 | kfree(slave_dev); 185 | } 186 | 187 | static void spislave_release(struct device *dev) 188 | { 189 | struct spislave *slave; 190 | 191 | pr_info("%s: function: release\n", DRIVER_NAME); 192 | 193 | slave = container_of(dev, struct spislave, dev); 194 | kfree(slave); 195 | } 196 | 197 | static struct class spislave_class = { 198 | .name = "spi_slave", 199 | .owner = THIS_MODULE, 200 | .dev_release = spislave_release, 201 | }; 202 | 203 | struct spislave *spislave_alloc_slave(struct device *dev, unsigned int size) 204 | { 205 | struct spislave *slave; 206 | 207 | pr_info("%s: function: alloc slave\n", DRIVER_NAME); 208 | 209 | slave = kzalloc(size + sizeof(*slave), GFP_KERNEL); 210 | if (!slave) 211 | return NULL; 212 | 213 | device_initialize(&slave->dev); 214 | slave->dev.class = &spislave_class; 215 | slave->dev.parent = get_device(dev); 216 | dev_set_drvdata(&slave->dev, (void *)&slave[1]); 217 | 218 | return slave; 219 | } 220 | EXPORT_SYMBOL_GPL(spislave_alloc_slave); 221 | 222 | static struct spislave_device *spislave_register_device(struct spislave *slave, 223 | struct device_node *nc) 224 | { 225 | struct spislave_device *slave_dev; 226 | int ret; 227 | 228 | pr_info("%s: function: register device\n", DRIVER_NAME); 229 | 230 | dev_dbg(&slave->dev, "chid node is found: %s\n", nc->full_name); 231 | 232 | slave_dev = kzalloc(sizeof(*slave_dev), GFP_KERNEL); 233 | 234 | if (!slave_dev) { 235 | put_device(&slave->dev); 236 | return NULL; 237 | } 238 | 239 | slave_dev->slave = slave; 240 | slave_dev->dev.parent = &slave->dev; 241 | slave_dev->dev.bus = &spislave_bus_type; 242 | slave_dev->dev.release = spislave_dev_release; 243 | 244 | ret = of_modalias_node(nc, slave_dev->modalias, 245 | sizeof(slave_dev->modalias)); 246 | if (ret < 0) { 247 | dev_dbg(&slave->dev, "cannot find modalias\n"); 248 | return NULL; 249 | } 250 | 251 | of_node_get(nc); 252 | slave_dev->dev.of_node = nc; 253 | dev_set_name(&slave_dev->dev, "%s", nc->name); 254 | ret = device_register(&slave_dev->dev); 255 | if (ret) { 256 | dev_dbg(&slave->dev, "register child device erroc\n"); 257 | return NULL; 258 | } 259 | 260 | return slave_dev; 261 | } 262 | 263 | int spislave_register_devices(struct spislave *slave) 264 | { 265 | struct spislave_device *slave_dev; 266 | struct device_node *nc; 267 | 268 | pr_info("%s: function: register devices\n", DRIVER_NAME); 269 | 270 | if (!slave->dev.of_node) 271 | return -ENODEV; 272 | 273 | for_each_available_child_of_node(slave->dev.of_node, nc) { 274 | if (of_node_test_and_set_flag(nc, OF_POPULATED)) 275 | continue; 276 | 277 | slave_dev = spislave_register_device(slave, nc); 278 | if (IS_ERR(slave_dev)) 279 | dev_dbg(&slave->dev, 280 | "filed to create spislave device\n"); 281 | } 282 | return 0; 283 | } 284 | 285 | 286 | int spislave_register_slave(struct spislave *slave, struct device *dev) 287 | { 288 | int ret = 0; 289 | struct device_node *node; 290 | 291 | pr_info("%s: function: register slave\n", DRIVER_NAME); 292 | 293 | if (!dev) 294 | return -ENODEV; 295 | 296 | node = dev->of_node; 297 | slave->dev.of_node = dev ? node : NULL; 298 | dev_set_name(&slave->dev, "%s", node->name); 299 | 300 | ret = device_add(&slave->dev); 301 | if (ret < 0) { 302 | dev_dbg(&slave->dev, "device add error\n"); 303 | return -ENODEV; 304 | } 305 | 306 | ret = spislave_register_devices(slave); 307 | if (ret < 0) 308 | dev_dbg(&slave->dev, " child device add erroc\n"); 309 | 310 | return ret; 311 | } 312 | 313 | int devm_spislave_register_slave(struct device *dev, 314 | struct spislave *slave) 315 | { 316 | int ret = 0; 317 | struct spislave **ptr; 318 | 319 | pr_info("%s: function: devm register slave\n", DRIVER_NAME); 320 | 321 | ptr = devres_alloc(devm_spislave_unregister_slave, sizeof(*ptr), 322 | GFP_KERNEL); 323 | if (!ptr) { 324 | dev_dbg(&slave->dev, "devers alloc error\n"); 325 | return -ENOMEM; 326 | } 327 | 328 | ret = spislave_register_slave(slave, dev); 329 | if (!ret) { 330 | *ptr = slave; 331 | devres_add(dev, ptr); 332 | } else 333 | devres_free(ptr); 334 | 335 | return ret; 336 | } 337 | EXPORT_SYMBOL_GPL(devm_spislave_register_slave); 338 | 339 | void spislave_unregister_device(struct spislave_device *sdev) 340 | { 341 | pr_info("%s: function: function: unregister device\n", DRIVER_NAME); 342 | 343 | if (!sdev) 344 | return; 345 | 346 | if (sdev->dev.of_node) 347 | of_node_clear_flag(sdev->dev.of_node, OF_POPULATED); 348 | 349 | device_unregister(&sdev->dev); 350 | } 351 | EXPORT_SYMBOL_GPL(spislave_unregister_device); 352 | /*============================================================================*/ 353 | 354 | static const struct spislave_device_id *spislave_match_id(const struct 355 | spislave_device_id * id, const struct spislave_device *sdev) 356 | { 357 | while (id->name[0]) { 358 | if (!strcmp(sdev->modalias, id->name)) 359 | return id; 360 | id++; 361 | } 362 | return NULL; 363 | } 364 | 365 | static int spislave_device_match(struct device *dev, 366 | struct device_driver *drv) 367 | { 368 | const struct spislave_driver *sdrv; 369 | const struct spislave_device *sdev; 370 | 371 | pr_info("%s: function: device match\n", DRIVER_NAME); 372 | 373 | sdrv = to_spislave_drv(drv); 374 | sdev = to_spislave_dev(dev); 375 | 376 | if (of_driver_match_device(dev, drv)) 377 | return 1; 378 | 379 | if (sdrv->id_table) 380 | return !!spislave_match_id(sdrv->id_table, sdev); 381 | 382 | return strcmp(sdev->modalias, sdrv->driver.name) == 0; 383 | } 384 | 385 | struct bus_type spislave_bus_type = { 386 | .name = "spislave", 387 | .match = spislave_device_match, 388 | }; 389 | 390 | static int __init spislave_init(void) 391 | { 392 | int ret = 0; 393 | 394 | pr_info("%s: function: init\n", DRIVER_NAME); 395 | 396 | ret = bus_register(&spislave_bus_type); 397 | if (ret < 0) 398 | return ret; 399 | 400 | ret = class_register(&spislave_class); 401 | if (ret < 0) 402 | bus_unregister(&spislave_bus_type); 403 | 404 | return ret; 405 | } 406 | 407 | static void __exit spislave_exit(void) 408 | { 409 | pr_info("%s: function: exit\n", DRIVER_NAME); 410 | 411 | bus_unregister(&spislave_bus_type); 412 | } 413 | 414 | module_init(spislave_init); 415 | module_exit(spislave_exit); 416 | 417 | MODULE_LICENSE("GPL v2"); 418 | MODULE_AUTHOR("Patryk Mezydlo, "); 419 | MODULE_DESCRIPTION("bus driver"); 420 | MODULE_VERSION("1.0"); 421 | -------------------------------------------------------------------------------- /driver/spi-slave-core.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface between SPI slave-side drivers and SPI slave infrastructure. 3 | * 4 | * Copyright (C) 2016 Patryk Mężydło 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #ifndef SPI_SLAVE_CORE_H 12 | #define SPI_SLAVE_CORE_H 13 | 14 | #define SPISLAVE_NAME_SIZE 32 15 | 16 | extern struct bus_type spislave_bus_type; 17 | 18 | struct spislave_message { 19 | void __iomem *tx; 20 | u32 tx_actual_length; 21 | void __iomem *rx; 22 | u32 rx_actual_length; 23 | u8 mode; 24 | u32 bits_per_word; 25 | u32 buf_depth; 26 | u32 max_speed; 27 | u8 lsb_first; 28 | 29 | wait_queue_head_t wait; 30 | spinlock_t wait_lock; 31 | struct mutex msg_lock; 32 | }; 33 | 34 | struct spislave { 35 | struct device dev; 36 | struct spislave_message *msg; 37 | void *spislave_gadget; 38 | 39 | int (*transfer_msg)(struct spislave *slave); 40 | void (*clear_msg)(struct spislave *slave); 41 | }; 42 | 43 | struct spislave_device_id { 44 | char name[SPISLAVE_NAME_SIZE]; 45 | kernel_ulong_t driver_data; 46 | }; 47 | 48 | struct spislave_device { 49 | struct device dev; 50 | struct spislave *slave; 51 | char modalias[SPISLAVE_NAME_SIZE]; 52 | }; 53 | 54 | struct spislave_driver { 55 | const struct spislave_device_id *id_table; 56 | int (*probe)(struct spislave_device *spi); 57 | int (*remove)(struct spislave_device *spi); 58 | struct device_driver driver; 59 | }; 60 | 61 | extern struct spislave_message *spislave_msg_alloc(struct spislave *slave); 62 | extern void spislave_msg_remove(struct spislave *slave); 63 | extern int spislave_transfer_msg(struct spislave *slave); 64 | 65 | extern int spislave_register_driver(struct spislave_driver *sdrv); 66 | extern void spislave_unregister_driver(struct spislave_driver *sdrv); 67 | extern int devm_spislave_register_slave(struct device *dev, 68 | struct spislave *slave); 69 | extern void spislave_unregister_device(struct spislave_device *dev); 70 | 71 | static inline void *spislave_get_drv_data(struct spislave_device *sdev) 72 | { 73 | return dev_get_drvdata(&sdev->dev); 74 | } 75 | 76 | static inline void spislave_set_drv_data(struct spislave_device *sdev, 77 | void *data) 78 | { 79 | dev_set_drvdata(&sdev->dev, data); 80 | } 81 | 82 | static inline void *spislave_get_slave_data(struct spislave *slave) 83 | { 84 | return dev_get_drvdata(&slave->dev); 85 | } 86 | 87 | static inline void spislave_set_slave_data(struct spislave *slave, 88 | void *data) 89 | { 90 | dev_set_drvdata(&slave->dev, data); 91 | } 92 | 93 | extern struct spislave *spislave_alloc_slave(struct device *dev, 94 | unsigned int size); 95 | static inline struct spislave_device *to_spislave_dev(struct device *dev) 96 | { 97 | return container_of(dev, struct spislave_device, dev); 98 | } 99 | 100 | static inline struct spislave_driver *to_spislave_drv(struct device_driver *drv) 101 | { 102 | return container_of(drv, struct spislave_driver, driver); 103 | } 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /driver/spi-slave-debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPI slave driver for debugging API 3 | * 4 | * Copyright (C) 2016 Patryk Mężydło 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define DRIVER_NAME "spi-slave-debug" 20 | 21 | #include "spi-slave-core.h" 22 | 23 | const struct of_device_id debspi_slave_of_match[] = { 24 | { 25 | .compatible = "spislave,spi-slave-debug", 26 | }, 27 | { } 28 | }; 29 | MODULE_DEVICE_TABLE(of, debspi_slave_of_match); 30 | 31 | static int debspi_slave_probe(struct platform_device *pdev) 32 | { 33 | pr_info("debug driver probe"); 34 | return 0; 35 | } 36 | 37 | static int debspi_slave_remove(struct platform_device *pdev) 38 | { 39 | pr_info("debug driver remove"); 40 | return 0; 41 | } 42 | 43 | static struct platform_driver debspi_slave_driver = { 44 | .probe = debspi_slave_probe, 45 | .remove = debspi_slave_remove, 46 | .driver = { 47 | .name = DRIVER_NAME, 48 | .of_match_table = of_match_ptr(debspi_slave_of_match), 49 | }, 50 | }; 51 | 52 | module_platform_driver(debspi_slave_driver); 53 | 54 | MODULE_LICENSE("GPL v2"); 55 | MODULE_AUTHOR("Patryk Mezydlo, "); 56 | MODULE_DESCRIPTION("SPI slave driver for debugging API"); 57 | MODULE_VERSION("1.0"); 58 | -------------------------------------------------------------------------------- /driver/spi-slave-dev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple userspace interface to SPI devices in slave mode. 3 | * 4 | * Copyright (C) 2016 Patryk Mężydło 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "spi-slave-dev.h" 27 | #include "spi-slave-core.h" 28 | 29 | #define DRIVER_NAME "spislavedev" 30 | #define SPISLAVE_MAJOR 156 31 | #define SPISLAVE_MAX_MINOR 64 32 | 33 | static int buf_depth = 64; 34 | module_param(buf_depth, int, S_IRUGO | S_IWUSR); 35 | MODULE_PARM_DESC(buf_depth, "Size of each tx and rx buffer[default 64 bytes]"); 36 | 37 | static LIST_HEAD(spislave_dev_list); 38 | static struct class *spislave_class; 39 | static DEFINE_MUTEX(spislave_dev_list_lock); 40 | static DEFINE_IDR(spislave_idr); 41 | static DEFINE_SPINLOCK(spislave_idr_lock); 42 | 43 | struct spislave_data { 44 | dev_t devt; 45 | struct list_head device_entry; 46 | unsigned int users; 47 | struct spislave *slave; 48 | int id; 49 | }; 50 | 51 | static ssize_t spislave_read(struct file *filp, char __user *buf, size_t count, 52 | loff_t *f_pos) 53 | { 54 | struct spislave_data *data = filp->private_data; 55 | struct spislave *slave = data->slave; 56 | struct spislave_message *msg = slave->msg; 57 | ssize_t status; 58 | unsigned long missing; 59 | DECLARE_WAITQUEUE(wait, current); 60 | unsigned long flags; 61 | int ret = 0; 62 | 63 | msg->mode |= SPISLAVE_TRM; 64 | msg->mode &= ~SPISLAVE_RM; 65 | 66 | pr_info("%s: function: read\n", DRIVER_NAME); 67 | 68 | ret = spislave_transfer_msg(slave); 69 | if (ret < 0) 70 | status = -EFAULT; 71 | 72 | spin_lock_irqsave(&msg->wait_lock, flags); 73 | 74 | if (filp->f_flags & O_NONBLOCK) { 75 | spin_unlock_irqrestore(&msg->wait_lock, flags); 76 | return -EAGAIN; 77 | } 78 | 79 | add_wait_queue(&msg->wait, &wait); 80 | for (;;) { 81 | set_current_state(TASK_INTERRUPTIBLE); 82 | if (signal_pending(current)) 83 | break; 84 | 85 | spin_unlock_irqrestore(&msg->wait_lock, flags); 86 | schedule(); 87 | spin_lock_irqsave(&msg->wait_lock, flags); 88 | } 89 | set_current_state(TASK_RUNNING); 90 | remove_wait_queue(&msg->wait, &wait); 91 | spin_unlock_irqrestore(&msg->wait_lock, flags); 92 | 93 | mutex_lock(&msg->msg_lock); 94 | 95 | if (count > msg->buf_depth) 96 | return -EMSGSIZE; 97 | 98 | if (!msg->rx) { 99 | msg->rx = kzalloc(msg->buf_depth, GFP_KERNEL); 100 | if (!msg->rx) 101 | return -ENOMEM; 102 | } 103 | 104 | status = count; 105 | missing = copy_to_user(buf, msg->rx, msg->rx_actual_length); 106 | if (missing == status) 107 | status = -EFAULT; 108 | else 109 | status = status - missing; 110 | 111 | mutex_unlock(&msg->msg_lock); 112 | 113 | return status; 114 | } 115 | 116 | static ssize_t spislave_write(struct file *filp, const char __user *buf, 117 | size_t count, loff_t *f_pos) 118 | { 119 | ssize_t status = 0; 120 | int ret = 0; 121 | unsigned long missing; 122 | struct spislave_data *data = filp->private_data; 123 | struct spislave *slave = data->slave; 124 | struct spislave_message *msg = slave->msg; 125 | 126 | pr_info("%s: function: write\n", DRIVER_NAME); 127 | 128 | mutex_lock(&msg->msg_lock); 129 | 130 | if (!msg->tx) { 131 | msg->tx = kzalloc(msg->buf_depth, GFP_KERNEL); 132 | if (!msg->tx) 133 | return -ENOMEM; 134 | } else 135 | memset(msg->tx, 0, msg->buf_depth); 136 | 137 | if (!msg->rx) { 138 | msg->rx = kzalloc(msg->buf_depth, GFP_KERNEL); 139 | if (!msg->rx) 140 | return -ENOMEM; 141 | } 142 | 143 | if (count > msg->buf_depth) 144 | return -EMSGSIZE; 145 | 146 | missing = copy_from_user(msg->tx, buf, count); 147 | 148 | if (missing == 0) 149 | status = count; 150 | else 151 | status = -EFAULT; 152 | 153 | mutex_unlock(&msg->msg_lock); 154 | 155 | msg->mode |= SPISLAVE_TRM; 156 | msg->mode &= ~SPISLAVE_TM; 157 | 158 | ret = spislave_transfer_msg(slave); 159 | if (ret < 0) { 160 | status = -EFAULT; 161 | pr_info("%s: function: write - status:%d", DRIVER_NAME, status); 162 | } 163 | 164 | return status; 165 | } 166 | 167 | static int spislave_release(struct inode *inode, struct file *filp) 168 | { 169 | struct spislave_data *data = filp->private_data; 170 | struct spislave *slave = data->slave; 171 | struct spislave_message *msg = slave->msg; 172 | 173 | pr_info("%s: function: release\n", DRIVER_NAME); 174 | 175 | mutex_lock(&spislave_dev_list_lock); 176 | 177 | kfree(msg->tx); 178 | kfree(msg->rx); 179 | 180 | spislave_msg_remove(slave); 181 | 182 | data->users--; 183 | mutex_unlock(&spislave_dev_list_lock); 184 | 185 | return 0; 186 | } 187 | 188 | static int spislave_open(struct inode *inode, struct file *filp) 189 | { 190 | struct spislave_data *data; 191 | struct spislave *slave; 192 | struct spislave_message *msg; 193 | int ret = -ENXIO; 194 | 195 | pr_info("%s: function: open\n", DRIVER_NAME); 196 | 197 | mutex_lock(&spislave_dev_list_lock); 198 | 199 | list_for_each_entry(data, &spislave_dev_list, device_entry) { 200 | if (data->devt == inode->i_rdev) { 201 | ret = 0; 202 | break; 203 | } 204 | } 205 | 206 | if (ret) { 207 | mutex_unlock(&spislave_dev_list_lock); 208 | return ret; 209 | } 210 | 211 | data->users++; 212 | if (data->users > 1) 213 | return -EBUSY; 214 | 215 | filp->private_data = data; 216 | nonseekable_open(inode, filp); 217 | slave = data->slave; 218 | 219 | msg = spislave_msg_alloc(slave); 220 | if (!msg) 221 | ret = -ENOMEM; 222 | msg->buf_depth = buf_depth; 223 | 224 | mutex_unlock(&spislave_dev_list_lock); 225 | 226 | return 0; 227 | } 228 | 229 | static long spislave_ioctl(struct file *filp, unsigned int cmd, 230 | unsigned long arg) 231 | { 232 | struct spislave_data *data = filp->private_data; 233 | struct spislave *slave = data->slave; 234 | struct spislave_message *msg = slave->msg; 235 | 236 | struct spislave_ioctl_transfer *ioctl_msg; 237 | u32 size_msg; 238 | 239 | int ret = 0; 240 | 241 | pr_info("%s: function: ioctl\n", DRIVER_NAME); 242 | 243 | mutex_lock(&msg->msg_lock); 244 | 245 | /* 246 | * FIXME: check arg and cmd argument, if cmd is ioctl type 247 | */ 248 | 249 | /* 250 | * FIXME: check all ioctl inputs and outputs 251 | */ 252 | 253 | switch (cmd) { 254 | case SPISLAVE_RD_TX_ACTUAL_LENGTH: 255 | ret = __put_user(msg->tx_actual_length, (__u32 __user *)arg); 256 | break; 257 | 258 | case SPISLAVE_RD_RX_ACTUAL_LENGTH: 259 | ret = __put_user(msg->rx_actual_length, (__u32 __user *)arg); 260 | break; 261 | 262 | case SPISLAVE_RD_BITS_PER_WORD: 263 | ret = __put_user(msg->bits_per_word, (__u32 __user *)arg); 264 | break; 265 | 266 | case SPISLAVE_RD_MODE: 267 | ret = __put_user(msg->mode, (__u32 __user *)arg); 268 | break; 269 | 270 | case SPISLAVE_RD_MAX_SPEED: 271 | ret = __put_user(msg->max_speed, (__u32 __user *)arg); 272 | break; 273 | 274 | case SPISLAVE_WR_BITS_PER_WORD: 275 | ret = __get_user(msg->bits_per_word, (__u32 __user *)arg); 276 | break; 277 | 278 | case SPISLAVE_WR_MODE: 279 | ret = __get_user(msg->mode, (__u32 __user *)arg); 280 | break; 281 | 282 | case SPISLAVE_WR_MAX_SPEED: 283 | ret = __get_user(msg->max_speed, (__u32 __user *)arg); 284 | break; 285 | 286 | default: 287 | 288 | /* 289 | * TODO: add support for full-duplex transfer 290 | * message uses own structures which located in 291 | * spi-slave-dev.h, 292 | * 293 | * need translate ioctl message to spi slave message standards 294 | */ 295 | 296 | size_msg = _IOC_SIZE(cmd); 297 | pr_info("%s: ioctl size_msg:%d/n", DRIVER_NAME, size_msg); 298 | 299 | ioctl_msg = kzalloc(size_msg, GFP_KERNEL); 300 | 301 | if (__copy_from_user(ioctl_msg, 302 | (struct spilave_ioctl_transfer __user *)arg, 303 | size_msg)) { 304 | kfree(ioctl_msg); 305 | return -EFAULT; 306 | } 307 | 308 | pr_info("%s: ioctl msg: mode:%d\n", DRIVER_NAME, 309 | ioctl_msg->mode); 310 | pr_info("%s: ioctl max_speed:%d\n", DRIVER_NAME, 311 | ioctl_msg->max_speed); 312 | pr_info("%s: ioctl bits_per_word:%d\n", DRIVER_NAME, 313 | ioctl_msg->bits_per_word); 314 | pr_info("%s: ioctl tx actual_length:%d\n", DRIVER_NAME, 315 | ioctl_msg->tx_actual_length); 316 | pr_info("%s: ioctl rx actual_length:%d\n", DRIVER_NAME, 317 | ioctl_msg->rx_actual_length); 318 | 319 | break; 320 | } 321 | 322 | mutex_unlock(&msg->msg_lock); 323 | return ret; 324 | } 325 | 326 | static unsigned int spislave_event_poll(struct file *filp, 327 | struct poll_table_struct *wait) 328 | { 329 | struct spislave_data *data = filp->private_data; 330 | struct spislave *slave = data->slave; 331 | struct spislave_message *msg = slave->msg; 332 | 333 | pr_info("%s: function: poll\n", DRIVER_NAME); 334 | 335 | poll_wait(filp, &msg->wait, wait); 336 | if (msg->rx_actual_length > 0) 337 | return POLLIN | POLLRDNORM; 338 | 339 | return 0; 340 | } 341 | 342 | static const struct file_operations spislave_fops = { 343 | .owner = THIS_MODULE, 344 | .open = spislave_open, 345 | .read = spislave_read, 346 | .write = spislave_write, 347 | .release = spislave_release, 348 | .unlocked_ioctl = spislave_ioctl, 349 | .poll = spislave_event_poll, 350 | }; 351 | 352 | static int spislave_probe(struct spislave_device *spi) 353 | { 354 | int ret = 0; 355 | struct spislave_data *data; 356 | struct spislave *slave; 357 | struct device *dev; 358 | struct device_node *node; 359 | 360 | pr_info("%s: function: probe\n", DRIVER_NAME); 361 | 362 | data = kzalloc(sizeof(*data), GFP_KERNEL); 363 | if (!data) 364 | return -ENODEV; 365 | 366 | slave = spi->slave; 367 | data->slave = slave; 368 | node = spi->dev.of_node; 369 | 370 | if (!slave) { 371 | ret = -ENODEV; 372 | goto err_out; 373 | } 374 | 375 | INIT_LIST_HEAD(&data->device_entry); 376 | 377 | mutex_lock(&spislave_dev_list_lock); 378 | spin_lock(&spislave_idr_lock); 379 | 380 | ret = idr_alloc(&spislave_idr, data, 0, SPISLAVE_MAX_MINOR, 381 | GFP_KERNEL); 382 | 383 | spin_unlock(&spislave_idr_lock); 384 | 385 | if (ret < 0) { 386 | ret = -EBUSY; 387 | goto err_out; 388 | } 389 | 390 | data->id = ret; 391 | data->users = 0; 392 | data->devt = MKDEV(SPISLAVE_MAJOR, data->id); 393 | dev = device_create(spislave_class, &spi->dev, data->devt, data, "%s", 394 | node->name); 395 | 396 | if (IS_ERR(&dev)) { 397 | ret = PTR_ERR(dev); 398 | goto err_out; 399 | } 400 | 401 | list_add(&data->device_entry, &spislave_dev_list); 402 | spislave_set_drv_data(spi, data); 403 | mutex_unlock(&spislave_dev_list_lock); 404 | 405 | return 0; 406 | 407 | err_out: 408 | kfree(data); 409 | mutex_unlock(&spislave_dev_list_lock); 410 | return ret; 411 | } 412 | 413 | static int spislave_remove(struct spislave_device *spi) 414 | { 415 | struct spislave_data *data = spislave_get_drv_data(spi); 416 | 417 | pr_info("%s: function: remove\n", DRIVER_NAME); 418 | 419 | data->slave = NULL; 420 | 421 | mutex_lock(&spislave_dev_list_lock); 422 | list_del(&data->device_entry); 423 | mutex_unlock(&spislave_dev_list_lock); 424 | 425 | device_destroy(spislave_class, data->devt); 426 | 427 | kfree(data); 428 | 429 | return 0; 430 | } 431 | 432 | static const struct of_device_id spislave_dt_ids[] = { 433 | { .compatible = "linux,spislave"}, 434 | {}, 435 | }; 436 | MODULE_DEVICE_TABLE(of, spislave_dt_ids); 437 | 438 | static struct spislave_driver slave_driver = { 439 | .driver = { 440 | .name = DRIVER_NAME, 441 | .of_match_table = of_match_ptr(spislave_dt_ids), 442 | }, 443 | .probe = spislave_probe, 444 | .remove = spislave_remove, 445 | }; 446 | 447 | static int __init spislave_init(void) 448 | { 449 | int ret = 0; 450 | 451 | pr_info("%s: function: init\n", DRIVER_NAME); 452 | 453 | ret = register_chrdev(SPISLAVE_MAJOR, DRIVER_NAME, &spislave_fops); 454 | if (ret < 0) 455 | return ret; 456 | 457 | spislave_class = class_create(THIS_MODULE, DRIVER_NAME); 458 | if (IS_ERR(spislave_class)) { 459 | unregister_chrdev(SPISLAVE_MAJOR, DRIVER_NAME); 460 | return PTR_ERR(spislave_class); 461 | } 462 | 463 | ret = spislave_register_driver(&slave_driver); 464 | if (ret) { 465 | class_unregister(spislave_class); 466 | class_destroy(spislave_class); 467 | return ret; 468 | } 469 | 470 | idr_init(&spislave_idr); 471 | 472 | return ret; 473 | } 474 | 475 | static void __exit spislave_exit(void) 476 | { 477 | 478 | pr_info("%s: function: exit\n", DRIVER_NAME); 479 | 480 | idr_destroy(&spislave_idr); 481 | spislave_unregister_driver(&slave_driver); 482 | class_destroy(spislave_class); 483 | unregister_chrdev(SPISLAVE_MAJOR, DRIVER_NAME); 484 | } 485 | 486 | module_init(spislave_init); 487 | module_exit(spislave_exit); 488 | 489 | MODULE_LICENSE("GPL v2"); 490 | MODULE_AUTHOR("Patryk Mezydlo, "); 491 | MODULE_DESCRIPTION("User mode SPI slave device interface"); 492 | MODULE_VERSION("1.0"); 493 | -------------------------------------------------------------------------------- /driver/spi-slave-dev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * IOCTL commands for SPI userspace interface in slave mode. 3 | * 4 | * Copyright (C) 2016 Patryk Mężydło 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #ifndef SPISLAVE_H 12 | #define SPISLAVE_H 13 | 14 | #include 15 | 16 | #define SPISLAVE_IOCTL_MAGIC 'k' 17 | 18 | #define SPISLAVE_SLAVE 0x01 19 | #define SPISLAVE_CPHA 0x02 20 | #define SPISLAVE_CPOL 0x04 21 | #define SPISLAVE_NO_CS 0x08 22 | #define SPISLAVE_CS_HIGH 0x10 23 | #define SPISLAVE_LSB_FIRST 0x20 24 | #define SPISLAVE_RM 0x40 25 | #define SPISLAVE_TM 0x80 26 | #define SPISLAVE_TRM 0xC0 27 | 28 | struct spislave_ioctl_transfer { 29 | __u64 tx_buf; 30 | __u64 rx_buf; 31 | 32 | __u32 tx_actual_length; 33 | __u32 rx_actual_length; 34 | 35 | __u32 mode; 36 | __u32 max_speed; 37 | __u8 bits_per_word; 38 | }; 39 | 40 | #define SPISLAVE_MSGSIZE(N) \ 41 | ((((N) * (sizeof(struct spislave_ioctl_transfer))) \ 42 | < (1 << _IOC_SIZEBITS)) \ 43 | ? ((N) * (sizeof(struct spislave_ioctl_transfer))) : 0) 44 | 45 | #define SPISLAVE_MESSAGE(N) __IOC(SPISLAVE_IOCTL_MAGIC, 0, \ 46 | char[SPISLAVE_MSGSIZE(N)]) 47 | 48 | #define SPISLAVE_RD_BITS_PER_WORD _IOR(SPISLAVE_IOCTL_MAGIC, 1, __u8) 49 | #define SPISLAVE_WR_BITS_PER_WORD _IOW(SPISLAVE_IOCTL_MAGIC, 1, __u8) 50 | 51 | #define SPISLAVE_RD_MODE _IOR(SPISLAVE_IOCTL_MAGIC, 2, __u32) 52 | #define SPISLAVE_WR_MODE _IOW(SPISLAVE_IOCTL_MAGIC, 2, __u32) 53 | 54 | #define SPISLAVE_RD_MAX_SPEED _IOR(SPISLAVE_IOCTL_MAGIC, 3, __u32) 55 | #define SPISLAVE_WR_MAX_SPEED _IOW(SPISLAVE_IOCTL_MAGIC, 3, __u32) 56 | 57 | #define SPISLAVE_RD_TX_ACTUAL_LENGTH _IOR(SPISLAVE_IOCTL_MAGIC, 4, __u32) 58 | #define SPISLAVE_RD_RX_ACTUAL_LENGTH _IOR(SPISLAVE_IOCTL_MAGIC, 5, __u32) 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /python_lib/py_spislave.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "structmember.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "../driver/spi-slave-dev.h" 10 | 11 | #define MAX_PATH 4096 12 | 13 | /* Macros needed for Python 3*/ 14 | #ifndef PyInt_Check 15 | #define PyInt_Check PyLong_Check 16 | #define PyInt_FromLong PyLong_FromLong 17 | #define PyInt_AsLong PyLong_AsLong 18 | #define PyInt_Type PyLong_Type 19 | #endif 20 | 21 | /* 22 | * TODO: 23 | * add support for full-duplex transfer, 24 | */ 25 | 26 | PyDoc_STRVAR(SPIslave_doc, "This module defines an object type that allows\n" 27 | "SPIslave transactions on hosts running the Linux kernel. The host kernel\n" 28 | "must have SPIslave core and SPIslave device interface.\n" 29 | "Author: Patryk Mezydlo" 30 | "E-mail: mezydlo.p@gmail.com"); 31 | 32 | PyDoc_STRVAR(SPIslave_open_doc,"open(device)\n\n" 33 | "Connects the object to the specified\n" 34 | "SPIslave device.\n" 35 | "open(X) will open /dev/spislave"); 36 | 37 | PyDoc_STRVAR(SPIslave_close_doc,"close()\n\n" 38 | "Disconnects the object from the interface\n"); 39 | 40 | PyDoc_STRVAR(SPIslave_read_doc,"read(len) -> [values]\n\n" 41 | "Read len bytes from SPIslave device.\n"); 42 | 43 | PyDoc_STRVAR(SPIslave_write_doc,"write([values])\n\n" 44 | "Write bytes to SPIslave device.\n"); 45 | 46 | typedef struct { 47 | PyObject_HEAD; 48 | 49 | int fd; /*file descriptor /dev/spislaveX*/ 50 | uint8_t mode; 51 | uint32_t tx_actual_length; 52 | uint32_t rx_actual_length; 53 | uint32_t max_speed; 54 | uint8_t bits_per_word; 55 | } SPIslave; 56 | 57 | static PyObject *SPIslave_new(PyTypeObject *type, PyObject *args, 58 | PyObject *kwds) 59 | { 60 | SPIslave *self; 61 | 62 | self = (SPIslave *)type->tp_alloc(type, 0); 63 | if (self == NULL) 64 | return NULL; 65 | 66 | self->fd = -1; 67 | self->mode = 0; 68 | self->tx_actual_length = 0; 69 | self->rx_actual_length = 0; 70 | self->max_speed = 0; 71 | self->bits_per_word = 0; 72 | 73 | return (PyObject *)self; 74 | } 75 | 76 | static PyObject *SPIslave_open(SPIslave *self, PyObject *args) 77 | { 78 | int device; 79 | char path[MAX_PATH]; 80 | uint32_t tmp32; 81 | uint8_t tmp8; 82 | 83 | if (self == NULL) 84 | printf("self is NULL\n"); 85 | 86 | if (!PyArg_ParseTuple(args, "i", &device)) 87 | return NULL; 88 | 89 | if (snprintf(path, MAX_PATH, "/dev/spislave%d", device) >= MAX_PATH) { 90 | PyErr_SetString(PyExc_OverflowError, 91 | "Device number is invalid."); 92 | return NULL; 93 | } 94 | 95 | self->fd = open(path, O_RDWR, 0); 96 | if (self->fd == -1) { 97 | PyErr_SetFromErrno(PyExc_IOError); 98 | return NULL; 99 | } 100 | 101 | if (ioctl(self->fd, SPISLAVE_RD_MODE, &tmp8) == -1) { 102 | PyErr_SetFromErrno(PyExc_IOError); 103 | return NULL; 104 | } 105 | self->mode = tmp8; 106 | 107 | if (ioctl(self->fd, SPISLAVE_RD_MAX_SPEED, &tmp32) == -1) { 108 | PyErr_SetFromErrno(PyExc_IOError); 109 | return NULL; 110 | } 111 | self->max_speed = tmp32; 112 | 113 | if (ioctl(self->fd, SPISLAVE_RD_BITS_PER_WORD, &tmp8) == -1) { 114 | PyErr_SetFromErrno(PyExc_IOError); 115 | return NULL; 116 | } 117 | self->bits_per_word = tmp8; 118 | 119 | if (ioctl(self->fd, SPISLAVE_RD_TX_ACTUAL_LENGTH, &tmp32) == -1) { 120 | PyErr_SetFromErrno(PyExc_IOError); 121 | return NULL; 122 | } 123 | self->tx_actual_length = tmp32; 124 | 125 | if (ioctl(self->fd, SPISLAVE_RD_RX_ACTUAL_LENGTH, &tmp32) == -1) { 126 | PyErr_SetFromErrno(PyExc_IOError); 127 | return NULL; 128 | } 129 | self->rx_actual_length = tmp32; 130 | 131 | Py_RETURN_NONE; 132 | } 133 | 134 | static PyObject *SPIslave_close(SPIslave *self) 135 | { 136 | if ((self->fd != -1) && (close(self->fd) == -1)) { 137 | PyErr_SetFromErrno(PyExc_IOError); 138 | return NULL; 139 | } 140 | 141 | self->fd = -1; 142 | self->mode = 0; 143 | self->tx_actual_length = 0; 144 | self->rx_actual_length = 0; 145 | self->max_speed = 0; 146 | self->bits_per_word = 0; 147 | 148 | Py_RETURN_NONE; 149 | } 150 | 151 | static void SPIslave_dealloc(SPIslave *self) 152 | { 153 | PyObject *ref = SPIslave_close(self); 154 | 155 | printf("spi slave dealoc python mod"); 156 | Py_XDECREF(ref); 157 | } 158 | 159 | static PyObject *SPIslave_read(SPIslave *self, PyObject *args) 160 | { 161 | uint8_t rx[MAX_PATH]; 162 | int status, len, i; 163 | PyObject *list; 164 | 165 | if (!PyArg_ParseTuple(args, "i:read", &len)) 166 | return NULL; 167 | 168 | if (len < 1) 169 | len = 1; 170 | else if ((unsigned)len > sizeof(rx)) 171 | len = sizeof(rx); 172 | 173 | memset(rx, 0, sizeof(rx)); 174 | status = read(self->fd, &rx[0], len); 175 | 176 | if (status < 0) { 177 | PyErr_SetFromErrno(PyExc_IOError); 178 | return NULL; 179 | } 180 | 181 | if (status != len) { 182 | perror("short read"); 183 | return NULL; 184 | } 185 | 186 | list = PyList_New(len); 187 | 188 | for (i = 0; i < len; i++) { 189 | PyObject *val = Py_BuildValue("l", (long)rx[i]); 190 | 191 | PyList_SET_ITEM(list, i, val); 192 | } 193 | 194 | return list; 195 | } 196 | 197 | static char *wrmsg_list0 = "Empty argument list."; 198 | static char *wrmsg_listmax = "Argument list size exceeds %d bytes."; 199 | static char *wrmsg_val = "Non-Int/Long value in argument: %x."; 200 | 201 | static PyObject *SPIslave_write(SPIslave *self, PyObject *args) 202 | { 203 | int status; 204 | uint16_t i, len; 205 | uint8_t tx[MAX_PATH]; 206 | PyObject *obj, *seq; 207 | char wrmsg_text[MAX_PATH]; 208 | 209 | if (!PyArg_ParseTuple(args, "O:write", &obj)) 210 | return NULL; 211 | 212 | seq = PySequence_Fast(obj, "expected a sequence"); 213 | len = PySequence_Fast_GET_SIZE(obj); 214 | if (!seq || len <= 0) { 215 | PyErr_SetString(PyExc_OverflowError, wrmsg_list0); 216 | return NULL; 217 | } 218 | 219 | if (len > MAX_PATH) { 220 | snprintf(wrmsg_text, sizeof(wrmsg_text) - 1, wrmsg_listmax, 221 | MAX_PATH); 222 | PyErr_SetString(PyExc_OverflowError, wrmsg_text); 223 | return NULL; 224 | } 225 | 226 | for (i = 0; i < len; i++) { 227 | PyObject *val = PySequence_Fast_GET_ITEM(seq, i); 228 | 229 | if (PyInt_Check(val)) 230 | tx[i] = (__u8)PyInt_AsLong(val); 231 | else { 232 | snprintf(wrmsg_text, sizeof(wrmsg_text) - 1, wrmsg_val, 233 | val); 234 | PyErr_SetString(PyExc_TypeError, wrmsg_text); 235 | return NULL; 236 | } 237 | } 238 | 239 | Py_DECREF(seq); 240 | status = write(self->fd, &tx[0], len); 241 | 242 | if (status < 0) { 243 | PyErr_SetFromErrno(PyExc_IOError); 244 | return NULL; 245 | } 246 | 247 | if (status != len) { 248 | perror("short write"); 249 | return NULL; 250 | } 251 | 252 | Py_INCREF(Py_None); 253 | return Py_None; 254 | } 255 | 256 | static PyMethodDef SPIslave_methods[] = { 257 | {"open", 258 | (PyCFunction)SPIslave_open, 259 | METH_VARARGS | METH_KEYWORDS, 260 | SPIslave_open_doc}, 261 | 262 | {"close", 263 | (PyCFunction)SPIslave_close, 264 | METH_NOARGS, 265 | SPIslave_close_doc}, 266 | 267 | {"read", 268 | (PyCFunction)SPIslave_read, 269 | METH_VARARGS, 270 | SPIslave_read_doc}, 271 | 272 | {"write", 273 | (PyCFunction)SPIslave_write, 274 | METH_VARARGS, 275 | SPIslave_write_doc}, 276 | 277 | {NULL}, 278 | }; 279 | 280 | static int __SPIslave_set_mode(SPIslave *self, __u8 tmp) 281 | { 282 | if (ioctl(self->fd, SPISLAVE_WR_MODE, &tmp) == -1) { 283 | PyErr_SetFromErrno(PyExc_IOError); 284 | return -1; 285 | } 286 | return 0; 287 | } 288 | 289 | static PyObject *SPIslave_get_mode(SPIslave *self, void *closure) 290 | { 291 | PyObject *result = Py_BuildValue("i", self->mode); 292 | 293 | Py_INCREF(result); 294 | return result; 295 | } 296 | 297 | static int SPIslave_set_mode(SPIslave *self, PyObject *val, void *closure) 298 | { 299 | uint8_t tmp; 300 | 301 | if (val == NULL) { 302 | PyErr_SetString(PyExc_TypeError, "value is invalid"); 303 | return -1; 304 | } 305 | 306 | if (PyLong_Check(val)) { 307 | tmp = PyInt_AsLong(val); 308 | } else { 309 | PyErr_SetString(PyExc_TypeError, "value must be integer"); 310 | return -1; 311 | } 312 | 313 | if (tmp >= 0x3F) { 314 | PyErr_SetString(PyExc_TypeError, 315 | "the mode must be between 0 and 63."); 316 | return -1; 317 | } 318 | 319 | if (self->mode != tmp) { 320 | if (__SPIslave_set_mode(self, tmp) < 0) 321 | return -1; 322 | 323 | self->mode = tmp; 324 | } 325 | 326 | return 0; 327 | } 328 | 329 | static PyObject *SPIslave_get_bits_per_word(SPIslave *self, void *closure) 330 | { 331 | PyObject *result = Py_BuildValue("i", self->bits_per_word); 332 | 333 | Py_INCREF(result); 334 | return result; 335 | } 336 | 337 | static int SPIslave_set_bits_per_word(SPIslave *self, PyObject *val, 338 | void *closure) 339 | { 340 | uint8_t tmp; 341 | 342 | if (val == NULL) { 343 | PyErr_SetString(PyExc_TypeError, "value is invalid"); 344 | return -1; 345 | } 346 | 347 | if (PyLong_Check(val)) { 348 | tmp = PyInt_AsLong(val); 349 | } else { 350 | PyErr_SetString(PyExc_TypeError, "value must be integer"); 351 | return -1; 352 | } 353 | 354 | if (tmp > 64) { 355 | PyErr_SetString(PyExc_TypeError, 356 | "the bits per word must be between 0 and 64."); 357 | return -1; 358 | } 359 | 360 | if (self->bits_per_word != tmp) { 361 | if (ioctl(self->fd, SPISLAVE_WR_BITS_PER_WORD, &tmp) == -1) { 362 | PyErr_SetFromErrno(PyExc_IOError); 363 | return -1; 364 | } 365 | self->bits_per_word = tmp; 366 | } 367 | 368 | return 0; 369 | } 370 | 371 | static PyObject *SPIslave_get_max_speed(SPIslave *self, void *closure) 372 | { 373 | PyObject *result = Py_BuildValue("i", self->max_speed); 374 | 375 | Py_INCREF(result); 376 | return result; 377 | } 378 | 379 | static int SPIslave_set_max_speed(SPIslave *self, PyObject *val, void *closure) 380 | { 381 | uint8_t tmp; 382 | 383 | if (val == NULL) { 384 | PyErr_SetString(PyExc_TypeError, "value is invalid"); 385 | return -1; 386 | } 387 | 388 | if (PyLong_Check(val)) { 389 | tmp = PyInt_AsLong(val); 390 | } else { 391 | PyErr_SetString(PyExc_TypeError, "value must be integer"); 392 | return -1; 393 | } 394 | 395 | if (self->max_speed != tmp) { 396 | if (ioctl(self->fd, SPISLAVE_WR_MAX_SPEED, &tmp) == -1) { 397 | PyErr_SetFromErrno(PyExc_IOError); 398 | return -1; 399 | } 400 | self->max_speed = tmp; 401 | } 402 | 403 | return 0; 404 | } 405 | 406 | static PyObject *SPIslave_get_tx_actual_length(SPIslave *self, void *closure) 407 | { 408 | PyObject *result = Py_BuildValue("i", self->tx_actual_length); 409 | 410 | Py_INCREF(result); 411 | return result; 412 | } 413 | 414 | static PyObject *SPIslave_get_rx_actual_length(SPIslave *self, void *closure) 415 | { 416 | PyObject *result = Py_BuildValue("i", self->rx_actual_length); 417 | 418 | Py_INCREF(result); 419 | return result; 420 | } 421 | 422 | static PyObject *SPIslave_get_SLAVE(SPIslave *self, void *closure) 423 | { 424 | PyObject *result; 425 | 426 | if (self->mode & SPISLAVE_SLAVE) 427 | result = Py_True; 428 | else 429 | result = Py_False; 430 | 431 | Py_INCREF(result); 432 | return result; 433 | } 434 | 435 | static int SPIslave_set_SLAVE(SPIslave *self, PyObject *val, void *closure) 436 | { 437 | uint8_t tmp; 438 | 439 | if (val == NULL) { 440 | PyErr_SetString(PyExc_TypeError, "value is invalid"); 441 | return -1; 442 | } else if (!PyBool_Check(val)) { 443 | PyErr_SetString(PyExc_TypeError, "the slave must be boolean"); 444 | return -1; 445 | } 446 | 447 | if (val == Py_True) 448 | tmp = self->mode | SPISLAVE_SLAVE; 449 | else 450 | tmp = self->mode & ~SPISLAVE_SLAVE; 451 | 452 | if (__SPIslave_set_mode(self, tmp) < 0) 453 | return -1; 454 | 455 | self->mode = tmp; 456 | 457 | return 0; 458 | } 459 | 460 | static PyObject *SPIslave_get_CPOL(SPIslave *self, void *closure) 461 | { 462 | PyObject *result; 463 | 464 | if (self->mode & SPISLAVE_CPOL) 465 | result = Py_True; 466 | else 467 | result = Py_False; 468 | 469 | Py_INCREF(result); 470 | return result; 471 | } 472 | 473 | static int SPIslave_set_CPOL(SPIslave *self, PyObject *val, void *closure) 474 | { 475 | uint8_t tmp; 476 | 477 | if (val == NULL) { 478 | PyErr_SetString(PyExc_TypeError, "value is invalid"); 479 | return -1; 480 | } else if (!PyBool_Check(val)) { 481 | PyErr_SetString(PyExc_TypeError, "the CPOL must be boolean"); 482 | return -1; 483 | } 484 | 485 | if (val == Py_True) 486 | tmp = self->mode | SPISLAVE_CPOL; 487 | else 488 | tmp = self->mode & ~SPISLAVE_CPOL; 489 | 490 | if (__SPIslave_set_mode(self, tmp) < 0) 491 | return -1; 492 | 493 | self->mode = tmp; 494 | 495 | return 0; 496 | } 497 | 498 | static PyObject *SPIslave_get_CPHA(SPIslave *self, void *closure) 499 | { 500 | PyObject *result; 501 | 502 | if (self->mode & SPISLAVE_CPHA) 503 | result = Py_True; 504 | else 505 | result = Py_False; 506 | 507 | Py_INCREF(result); 508 | return result; 509 | } 510 | 511 | static int SPIslave_set_CPHA(SPIslave *self, PyObject *val, void *closure) 512 | { 513 | uint8_t tmp; 514 | 515 | if (val == NULL) { 516 | PyErr_SetString(PyExc_TypeError, "value is invalid"); 517 | return -1; 518 | } else if (!PyBool_Check(val)) { 519 | PyErr_SetString(PyExc_TypeError, "the CPHA must be boolean"); 520 | return -1; 521 | } 522 | 523 | if (val == Py_True) 524 | tmp = self->mode | SPISLAVE_CPHA; 525 | else 526 | tmp = self->mode & ~SPISLAVE_CPHA; 527 | 528 | if (__SPIslave_set_mode(self, tmp) < 0) 529 | return -1; 530 | 531 | self->mode = tmp; 532 | 533 | return 0; 534 | } 535 | 536 | static PyObject *SPIslave_get_NO_CS(SPIslave *self, void *closure) 537 | { 538 | PyObject *result; 539 | 540 | if (self->mode & SPISLAVE_NO_CS) 541 | result = Py_True; 542 | else 543 | result = Py_False; 544 | 545 | Py_INCREF(result); 546 | return result; 547 | } 548 | 549 | static int SPIslave_set_NO_CS(SPIslave *self, PyObject *val, void *closure) 550 | { 551 | uint8_t tmp; 552 | 553 | if (val == NULL) { 554 | PyErr_SetString(PyExc_TypeError, "value is invalid"); 555 | return -1; 556 | } else if (!PyBool_Check(val)) { 557 | PyErr_SetString(PyExc_TypeError, "the NO_CS must be boolean"); 558 | return -1; 559 | } 560 | 561 | if (val == Py_True) 562 | tmp = self->mode | SPISLAVE_NO_CS; 563 | else 564 | tmp = self->mode & ~SPISLAVE_NO_CS; 565 | 566 | if (__SPIslave_set_mode(self, tmp) < 0) 567 | return -1; 568 | 569 | self->mode = tmp; 570 | 571 | return 0; 572 | } 573 | 574 | static PyObject *SPIslave_get_CS_HIGH(SPIslave *self, void *closure) 575 | { 576 | PyObject *result; 577 | 578 | if (self->mode & SPISLAVE_CS_HIGH) 579 | result = Py_True; 580 | else 581 | result = Py_False; 582 | 583 | Py_INCREF(result); 584 | return result; 585 | } 586 | 587 | static int SPIslave_set_CS_HIGH(SPIslave *self, PyObject *val, void *closure) 588 | { 589 | uint8_t tmp; 590 | 591 | if (val == NULL) { 592 | PyErr_SetString(PyExc_TypeError, "value is invalid"); 593 | return -1; 594 | } else if (!PyBool_Check(val)) { 595 | PyErr_SetString(PyExc_TypeError, "the CS_HIGH must be boolean"); 596 | return -1; 597 | } 598 | 599 | if (val == Py_True) 600 | tmp = self->mode | SPISLAVE_CS_HIGH; 601 | else 602 | tmp = self->mode & ~SPISLAVE_CS_HIGH; 603 | 604 | if (__SPIslave_set_mode(self, tmp) < 0) 605 | return -1; 606 | 607 | self->mode = tmp; 608 | 609 | return 0; 610 | } 611 | 612 | static PyObject *SPIslave_get_LSB_FIRST(SPIslave *self, void *closure) 613 | { 614 | PyObject *result; 615 | 616 | if (self->mode & SPISLAVE_LSB_FIRST) 617 | result = Py_True; 618 | else 619 | result = Py_False; 620 | 621 | Py_INCREF(result); 622 | return result; 623 | } 624 | 625 | static int SPIslave_set_LSB_FIRST(SPIslave *self, PyObject *val, void *closure) 626 | { 627 | uint8_t tmp; 628 | 629 | if (val == NULL) { 630 | PyErr_SetString(PyExc_TypeError, "value is invalid"); 631 | return -1; 632 | } else if (!PyBool_Check(val)) { 633 | PyErr_SetString(PyExc_TypeError, 634 | "the LSB_FIRST must be boolean"); 635 | return -1; 636 | } 637 | 638 | if (val == Py_True) 639 | tmp = self->mode | SPISLAVE_LSB_FIRST; 640 | else 641 | tmp = self->mode & ~SPISLAVE_LSB_FIRST; 642 | 643 | if (__SPIslave_set_mode(self, tmp) < 0) 644 | return -1; 645 | 646 | self->mode = tmp; 647 | 648 | return 0; 649 | } 650 | 651 | static PyGetSetDef SPIslave_getset[] = { 652 | {"mode", 653 | (getter)SPIslave_get_mode, 654 | (setter)SPIslave_set_mode, 655 | "Defines basic setting of SPI transfer.\n"}, 656 | 657 | {"SLAVE", 658 | (getter)SPIslave_get_SLAVE, 659 | (setter)SPIslave_set_SLAVE, 660 | "Spi slave subsystem allows to work on both master or slave mode.\n" 661 | "You need to state which mode you want to use before transfer.\n" 662 | "Master mode is the main mode and is set by default.\n"}, 663 | 664 | {"CPHA", 665 | (getter)SPIslave_get_CPHA, 666 | (setter)SPIslave_set_CPHA, 667 | "That is CPHA=0 means sampling on the first clock edge, while CPHA=1\n" 668 | "means sampling on the second clock edge, regardless of whether that\n" 669 | "clock edge is rising or falling.\n"}, 670 | 671 | {"CPOL", 672 | (getter)SPIslave_get_CPOL, 673 | (setter)SPIslave_set_CPOL, 674 | "If CPOL is zero, than SCLK is normally low, and the first clock edge\n" 675 | "is a rising edge. If CPOL is one, SCLK is normally high, and the\n" 676 | "first clock edge is a falling edge.\n"}, 677 | 678 | {"NO_CS", 679 | (getter)SPIslave_get_NO_CS, 680 | (setter)SPIslave_set_NO_CS, 681 | "If NO_CS is zero, that CS is active, when NO_CS is set,\n" 682 | "CS line is not active.\n"}, 683 | 684 | {"CS_HIGH", 685 | (getter)SPIslave_get_CS_HIGH, 686 | (setter)SPIslave_set_CS_HIGH, 687 | "That is CS_HIGH means when the CS line state is high actives the\n" 688 | "receiving device.\n"}, 689 | 690 | {"LSB_FIRST", 691 | (getter)SPIslave_get_LSB_FIRST, 692 | (setter)SPIslave_set_LSB_FIRST, 693 | "The bit justification used to transfer spi words. Zero indicates\n" 694 | "MSB-first; other values indicate the less common LSB-first\n" 695 | "encoding. MSB-first is set by default.\n"}, 696 | 697 | {"max_speed", 698 | (getter)SPIslave_get_max_speed, 699 | (setter)SPIslave_set_max_speed, 700 | "Only in master mode. The maximum SPI transfer speed in Hz. The spi\n" 701 | "controller does not have to set this setting. Not every controller\n" 702 | "supports it.\n"}, 703 | 704 | {"bits_per_word", 705 | (getter)SPIslave_get_bits_per_word, 706 | (setter)SPIslave_set_bits_per_word, 707 | "Indicates the number of bits per one word. The typical numer of\n" 708 | "bits is 8, 16, 32. The default value is 8 bits.\n"}, 709 | 710 | {"tx_actual_length", 711 | (getter)SPIslave_get_tx_actual_length, 712 | (setter)NULL, "When you want to monitore how much bytes is in\n" 713 | "tx buffer.\n"}, 714 | 715 | {"rx_actual_length", 716 | (getter)SPIslave_get_rx_actual_length, 717 | (setter)NULL, "When you want to monitore how much bytes is in\n" 718 | "rx buffer."}, 719 | 720 | {NULL}, 721 | }; 722 | 723 | static int SPIslave_init(SPIslave *self, PyObject *args) 724 | { 725 | int device = -1; 726 | char path[MAX_PATH]; 727 | 728 | if (!PyArg_ParseTuple(args, "i", &device)) 729 | return -1; 730 | 731 | if (snprintf(path, MAX_PATH, "/dev/spislave%d", device) >= MAX_PATH) { 732 | PyErr_SetString(PyExc_OverflowError, 733 | "Device number is invalid."); 734 | return -1; 735 | } 736 | 737 | if (device >= 0) { 738 | SPIslave_open(self, args); 739 | if (PyErr_Occurred()) 740 | return -1; 741 | } 742 | 743 | return 0; 744 | } 745 | 746 | static PyTypeObject SPIslave_type = { 747 | PyVarObject_HEAD_INIT(NULL, 0) 748 | "SPIslave", /* tp_name */ 749 | sizeof(SPIslave), /* tp_basicsize */ 750 | 0, /* tp_itemsize */ 751 | (destructor)SPIslave_dealloc, /* tp_dealloc */ 752 | 0, /* tp_print */ 753 | 0, /* tp_getattr */ 754 | 0, /* tp_setattr */ 755 | 0, /* tp_compare */ 756 | 0, /* tp_repr */ 757 | 0, /* tp_as_number */ 758 | 0, /* tp_as_sequence */ 759 | 0, /* tp_as_mapping */ 760 | 0, /* tp_hash */ 761 | 0, /* tp_call */ 762 | 0, /* tp_str */ 763 | 0, /* tp_getattro */ 764 | 0, /* tp_setattro */ 765 | 0, /* tp_as_buffer */ 766 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 767 | 0, /* tp_doc */ 768 | 0, /* tp_traverse */ 769 | 0, /* tp_clear */ 770 | 0, /* tp_richcompare */ 771 | 0, /* tp_weaklistoffset */ 772 | 0, /* tp_iter */ 773 | 0, /* tp_iternext */ 774 | SPIslave_methods, /* tp_methods */ 775 | 0, /* tp_members */ 776 | SPIslave_getset, /* tp_getset */ 777 | 0, /* tp_base */ 778 | 0, /* tp_dict */ 779 | 0, /* tp_descr_get */ 780 | 0, /* tp_descr_set */ 781 | 0, /* tp_dictoffset */ 782 | (initproc)SPIslave_init, /* tp_init */ 783 | 0, /* tp_alloc */ 784 | SPIslave_new, /* tp_new */ 785 | }; 786 | 787 | 788 | static struct PyModuleDef SPIslave_module = { 789 | PyModuleDef_HEAD_INIT, 790 | "SPIslave", 791 | SPIslave_doc, 792 | -1, 793 | NULL, NULL, NULL, NULL, NULL 794 | }; 795 | 796 | PyMODINIT_FUNC PyInit_SPIslave(void) 797 | { 798 | PyObject *m; 799 | 800 | SPIslave_type.tp_new = PyType_GenericNew; 801 | if (PyType_Ready(&SPIslave_type) < 0) 802 | return NULL; 803 | 804 | m = PyModule_Create(&SPIslave_module); 805 | if (m == NULL) 806 | return NULL; 807 | 808 | Py_INCREF(&SPIslave_type); 809 | PyModule_AddObject(m, "SPIslave", (PyObject *)&SPIslave_type); 810 | 811 | return m; 812 | } 813 | -------------------------------------------------------------------------------- /python_lib/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup, Extension 2 | 3 | CFLAGS = ['-Wall', '-Werror', '-Wno-format-security'] 4 | 5 | setup (name = 'SPIslave', 6 | version = '1.0', 7 | author = 'Patryk Mezydlo', 8 | author_email = 'mezydlo.p@gmail.com', 9 | description = 'This is a demo package', 10 | url = 'https://github.com/pmezydlo/SPI_slave_driver_implementation', 11 | license = 'GPL v2', 12 | ext_modules = [Extension('SPIslave', sources = ['py_spislave.c'], extra_compile_args=CFLAGS)] ) 13 | -------------------------------------------------------------------------------- /slave_app/slave_app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "../driver/spi-slave-dev.h" 10 | 11 | #define TX_ARRAY_SIZE 8 12 | #define RX_ARRAY_SIZE 8 13 | 14 | static const char *device = "/dev/spislave0"; 15 | 16 | static uint32_t tx_actual_length; 17 | static uint32_t rx_actual_length; 18 | 19 | static uint32_t bits_per_word = 8; 20 | static uint8_t mode; 21 | static uint32_t bytes_per_load = 4; 22 | 23 | static int transfer_8bit(int fd) 24 | { 25 | int ret = 0; 26 | uint8_t tx[] = {'D', 'E', 'A', 'D', 'B', 'E', 'F', 'F'}; 27 | int i; 28 | 29 | ret = write(fd, tx, TX_ARRAY_SIZE); 30 | 31 | if (ret < 0) { 32 | printf("Failed to write massage!\n"); 33 | return -1; 34 | } 35 | 36 | printf("Transmit:\n"); 37 | 38 | for (i = 0; i < ret; i++) { 39 | printf("0x%02X ", tx[i]); 40 | 41 | if (i%8 == 7) 42 | printf("\n"); 43 | } 44 | 45 | return ret; 46 | } 47 | 48 | static int read_8bit(int fd) 49 | { 50 | uint8_t rx[RX_ARRAY_SIZE]; 51 | int ret; 52 | int i; 53 | uint32_t length; 54 | 55 | printf("Receive:\n"); 56 | 57 | ret = ioctl(fd, SPISLAVE_RD_RX_ACTUAL_LENGTH, &length); 58 | if (ret == -1) { 59 | printf("failed to read the length?\n"); 60 | return -1; 61 | } 62 | 63 | ret = read(fd, rx, length); 64 | if (ret < 0) { 65 | printf("failed to read the message!\n"); 66 | return -1; 67 | } 68 | 69 | for (i = 0; i < length; i++) { 70 | printf("0x%.2X ", rx[i]); 71 | 72 | if (i%8 == 7) 73 | printf("\n"); 74 | } 75 | return ret; 76 | } 77 | 78 | static int put_setting(int fd) 79 | { 80 | int ret = 0; 81 | 82 | ret = ioctl(fd, SPISLAVE_WR_BITS_PER_WORD, &bits_per_word); 83 | if (ret == -1) 84 | return -1; 85 | 86 | ret = ioctl(fd, SPISLAVE_WR_MODE, &mode); 87 | if (ret == -1) 88 | return -1; 89 | 90 | return ret; 91 | } 92 | 93 | static int get_setting(int fd) 94 | { 95 | int ret = 0; 96 | 97 | ret = ioctl(fd, SPISLAVE_RD_BITS_PER_WORD, &bits_per_word); 98 | if (ret == -1) 99 | return -1; 100 | 101 | ret = ioctl(fd, SPISLAVE_RD_RX_ACTUAL_LENGTH, &rx_actual_length); 102 | if (ret == -1) 103 | return -1; 104 | 105 | ret = ioctl(fd, SPISLAVE_RD_TX_ACTUAL_LENGTH, &tx_actual_length); 106 | if (ret == -1) 107 | return -1; 108 | 109 | ret = ioctl(fd, SPISLAVE_RD_MODE, &mode); 110 | if (ret == -1) 111 | return -1; 112 | 113 | return ret; 114 | } 115 | 116 | static void print_setting(void) 117 | { 118 | printf("TX length:%d, RX length:%d, Bits per word:%d\n", 119 | tx_actual_length, rx_actual_length, bits_per_word); 120 | printf("Mode:%d\n", mode); 121 | } 122 | 123 | static void print_usage(const char *prog) 124 | { 125 | printf("Usage: %s [-dbs?ewm]\n", prog); 126 | puts(" -d --device device to use (default /dev/spislave1\n" 127 | " -b --bpw bits per word (default 8 bits)\n" 128 | " -? --help print help\n" 129 | " -m --m slave mode 0-master, 1-slave\n" 130 | "\n"); 131 | exit(1); 132 | } 133 | 134 | static void parse_opts(int argc, char *argv[]) 135 | { 136 | while (1) { 137 | static const struct option lopts[] = { 138 | { "device", required_argument, 0, 'd' }, 139 | { "bpw", required_argument, 0, 'b' }, 140 | { "mode", required_argument, 0, 'm' }, 141 | { "help", no_argument, 0, '?' }, 142 | { NULL, 0, 0, 0 }, 143 | }; 144 | int c; 145 | 146 | c = getopt_long(argc, argv, "d:b:w:m:?", lopts, NULL); 147 | 148 | if (c == -1) 149 | break; 150 | 151 | switch (c) { 152 | case 'd': 153 | device = optarg; 154 | break; 155 | case 'b': 156 | bits_per_word = atoi(optarg); 157 | break; 158 | case 'm': 159 | mode = atoi(optarg); 160 | break; 161 | case '?': 162 | print_usage(device); 163 | break; 164 | default: 165 | break; 166 | } 167 | } 168 | } 169 | 170 | int main(int argc, char *argv[]) 171 | { 172 | int ret = 0; 173 | int fd; 174 | 175 | parse_opts(argc, argv); 176 | fd = open(device, O_RDWR); 177 | 178 | if (fd < 0) { 179 | printf("Failed to open the device!\n"); 180 | return -1; 181 | } 182 | 183 | printf("Open:%s\n", device); 184 | 185 | if (ret == -1) 186 | printf("Can't write bits per word\n"); 187 | 188 | print_setting(); 189 | 190 | ret = put_setting(fd); 191 | if (ret == -1) 192 | return -1; 193 | 194 | ret = transfer_8bit(fd); 195 | if (ret == -1) { 196 | printf("Failed to writes"); 197 | return -1; 198 | } 199 | 200 | ret = read_8bit(fd); 201 | if (ret < 0) { 202 | printf("Failed to reads!\n"); 203 | return -1; 204 | } 205 | 206 | close(fd); 207 | 208 | return ret; 209 | } 210 | -------------------------------------------------------------------------------- /slave_app/slave_app.py: -------------------------------------------------------------------------------- 1 | import SPIslave 2 | 3 | spi = SPIslave.SPIslave() 4 | spi.open(0) 5 | 6 | spi.bits_per_word = 8 7 | -------------------------------------------------------------------------------- /wiki/3layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/3layer.png -------------------------------------------------------------------------------- /wiki/McSPI_Technical_Reference_Manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/McSPI_Technical_Reference_Manual.pdf -------------------------------------------------------------------------------- /wiki/beagleboard_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/beagleboard_logo.png -------------------------------------------------------------------------------- /wiki/documentation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GSOC 7 | BeagleBoard.org 8 | eLinux.org 9 | 10 |

SPI slave driver implementation

11 |
    12 |
  1. Introduction
  2. 13 |
  3. Links
  4. 14 |
  5. Block diagram
  6. 15 |
  7. Slave driver in Linux architecture
  8. 16 |
  9. Problems
  10. 17 |
  11. Development timeline
  12. 18 |
  13. Final raport
  14. 19 |
  15. Photos from tests
  16. 20 |
  17. AM335x and McSPI data sheets
  18. 21 |
  19. Books about Linux and LKM
  20. 22 |
  21. Building driver on x86 platform
  22. 23 |
  23. Prepare BB board
  24. 24 |
  25. Installing driver on BB platform
  26. 25 |
  27. First data transfer
  28. 26 |
  29. Testing and wiring
  30. 27 |
28 | 29 |

Introduction

30 |

SPI slave driver implementation. The task is to create a driver controlling SPI hardware controller in slave mode, 31 | and to ensure optimal performance through the use of DMA and interrupt. Creating an easy to implement realization 32 | of SPI slave would definitely help the BeagleBone community members to write applications based on SPI much more 33 | easily. The first implementation of my protocol driver is going to example of a bidirectional data exchange. 34 | This application will provide the BeagleBone community with valuable experience and will be a good example 35 | of SPI slave. Hardware limitations make it impossible to perform any realization of the device using SPI slave. 36 | Sending away data to the master during one transaction is not possible. One transaction is enough to receive data 37 | by slave device. To receive and send back data, two transactions are needed. The data received from master device 38 | in a single transaction is transferred to the user after completing the transaction. The user's reply to received 39 | data is sent in the next transaction.

40 | 41 |
    42 |
  • Student: Patryk Mezydlo
  • 43 |
  • Mentors: Michael Welling, Andrew Bradford, Matt Porter
  • 44 |
  • Organization: BeagleBoard.org
  • 45 |
46 | 47 | 48 | 55 | 56 |

Block Diagram

57 | Block Diagram 58 | 59 | 60 |

Slave driver in Linux architecture

61 |

The framework in Linux should have 3 layers. In this way, there can be low level 62 | hardware interfaces to SPI hardware like McSPI in slave mode, a middle layer of common 63 | functions to call by protocol drivers, and then a top protocol driver layer which can be used to implement. 64 |

65 | 3 Layer Diagram 66 | 67 |

Problems

68 |

The main problems that don't allow us to create a fully universal generic SPI slave driver.

69 |
    70 |
  1. The master device can begin the transaction at any time, even if the slave device isn't ready to receive data.
  2. 71 |
  3. The transfer can be of any length, as standard SPI does not specify the frame’s length. 72 | Theoretically, the master device can send so much data that the slave device would stop receiving it because of memory overflow.
  4. 73 |
  5. SPI interface is a rather fast interface (10s or 100s of MHz), so with too fast clock the system can’t keep up with 74 | generated interrupts. SPI slave controller will hammer the system with interrupts and the system might become unresponsive.
  6. 75 |
  7. Even if you manage to pass data to userspace during the transaction, user will have no 76 | time to return the response that will be transferred to the master in the same transaction.
  8. 77 |
  9. When transaction is short but often repeated, slave device has no time to set the DMA transfer.
  10. 78 |
  11. The register with the contents of the data to be sent must be loaded by DMA before the transaction 79 | (high state on cs line). There is no way to load a register of broadcasting prior to the transaction, 80 | because you do not know the address of the data to come back before the transaction.
  12. 81 |
82 |

Solution:

83 |
    84 |
  1. The slave device, after installing the driver and DTS, will be ready to receive the data. 85 | The user must set the SPI slave device before the master device.
  2. 86 |
  3. During the installation of the driver, the user must declare the maximum depth of data buffer. 87 | The length is the same for tx and rx. Transfer may be shorter, but not longer than the declared length.
  4. 88 |
  5. The maximum speed of the slave mode specified in the documentation is 16Mhz as the maximum speed of SCLK line.
  6. 89 |
  7. Holding the response in memory and sending it in the next transaction solves the problem. 90 | The time between the transactions is greater than between the reception and transmission during a single transaction.
  8. 91 |
  9. Two-stage transactions allow to use DMA for loading and unloading mcspi buffer.
  10. 92 |
93 | 94 | 95 | 96 |

Development timeline

97 |

Each week I will devote a few hours to write the documentation.

98 |

    99 |
  1. 100 |

    Before the first week (before 24 May)

    101 |
    • create a GitHub repository ✔
    • 102 |
    • create eLinux page ✔
    • 103 |
    • create wiki page ✔
    • 104 |
    • prepare a few sd card with a Firmware Images ✔
    • 105 |
    • installation of necessary software ✔
    • 106 |
    • create .travis.yml script ✔
    • 107 |
    • create README.md ✔
    • 108 |
    • prepare video presentation ✔
    • 109 |
    • add license file ✔
    • 110 |
    • skeleton LKM ✔
    • 111 |
    • configure vim for kernel coding style ✔
    112 |
  2. 113 |
  3. 114 |

    Week 1(24 May - 31 May)

    115 |
    • create basic function: probe, remove, init exit ✔
    • 116 |
    • prepare test application(using spidev) ✔
    • 117 |
    • add more information about a driver works (describe main problems) ✔
    • 118 |
    • determinate McSPI base address (reading DTS device resources) ✔
    • 119 |
    • create device structure(keeps all the elements of the controller) ✔
    • 120 |
    • allocate memory for device ✔
    • 121 |
    • define McSPI register ✔
    • 122 |
    • function for reading and writing McSPI registers ✔
    • 123 |
    124 |
  4. 125 | 126 |
  5. 127 |

    Week 2(31 May - 07 June)

    128 |
    • set driver in slave mode ✔
    • 129 |
    • set the majority of important registers, except for those related to DMA, interrupts and fifo ✔
    • 130 |
    • familiarizing myself with the basics of McSPI controller ✔
    • 131 |
    • reading and writing McSPI registers ✔
    • 132 |
    • synchronization (pm_runtime) ✔
    • 133 |
    • clean up all kernel coding style issues ✔
    • 134 |
    • resolve all warnings ✔
    135 |
  6. 136 |
  7. 137 |

    Week 3(07 June - 14 June)

    138 |
    • familiarizing myself and setting McSPI transmit register ✔
    • 139 |
    • first test (echo); send back data which the driver receives from master ✔
    • 140 |
    • setting interrupt (I had a few problems with this) ✔
    • 141 |
    • adding function which waits for register bit ✔
    • 142 |
    • second test (interrupt); generating interrupt after 4 bits and checking how subtracted words count ✔
    • 143 |
    144 |
  8. 145 |
  9. 146 |

    Week 4(14 June - 21 June)

    147 |
    • transfer data from the FIFO to the driver buffer (for each word length) in both sides ✔
    • 148 |
    • finalized working on pio transfer ✔
    • 149 |
    • using tasklets for pio functions ✔
    • 150 |
    • a lot of tests (various lengths transfers and various length words(bits per word)) ✔
    • 151 |
    • beginning work on char driver as a framework for slave driver ✔
    • 152 |
    153 |
  10. 154 |
  11. 155 |

    Week 5(21 June - 28 June)

    156 |
    • registration and removal of char driver for each platform device ✔
    • 157 |
    • adding functions: open, read, write, release, etc ✔
    • 158 |
    • adding device to device list ✔
    • 159 |
    • create application for user on slave device ✔
    160 |
  12. 161 |
  13. 162 |

    Week 6(28 June - 05 July)

    163 |
      164 |
    • finalized work on char driver ✔
    • 165 |
    • improved prototype slave application and pio transfer ✔
    • 166 |
    • added ioctl ✔
    • 167 |
    • made thorough reconstruction of the driver ✔
    • 168 |
    169 |
  14. 170 |
  15. 171 |

    Week 7(05 July - 12 July)✔

    172 |
      173 |
    • finalized work on thorough reconstruction of the driver ✔
    • 174 |
    • first prototype test ✔
    • 175 |
    • finalized work on ioctl and poll ✔
    • 176 |
    • finalized work on prototype slave application and pio transfer ✔
    • 177 |
    • familiarizing myself with DMA ✔
    • 178 |
    • searched for bugs and repaired them ✔
    • 179 |
    • upgraded travis script ✔
    • 180 |
    • made a nice diagram ✔
    • 181 | 182 |
    183 |
  16. 184 |
  17. 185 |

    Week 8(12 July - 17 July)

      186 |
    • configuring dma channel ✔
    • 187 |
    • interrupt for dma ✔
    • 188 |
    • create callback functions ✔
    • 189 |
    • finally work on DMA
    • 190 |
    • tests with DMA without API (printk log)
    191 |
  18. 192 |
  19. 193 |

    Week 9(19 July - 26 July)

      194 |
    • work on DMA ✔
    • 195 |
    • configuration of the DMA transfer (filling config structure) ✔
    • 196 |
    • added callback function ✔
    • 197 |
    • enable and disable DMA request ✔
    • 198 |
    • DMA channel request (old way) ✔
    • 199 |
    • clearing DMA source ✔
    • 200 |
    201 |
  20. 202 |
  21. 203 |

    Week 10(26 July - 02 August)

      204 |
    • familiarizing myself with Buses ✔
    • 205 |
    • debugging dma ✔
    • 206 |
    • testing the driver in the 3.8.x version of the image (at the request of a colleague who is interested the driver) ✔
    • 207 |
    208 |
  22. 209 |
  23. 210 |

    Week 11(02 August - 09 August)

    211 |
    • reated a new driver (spi-slave-core.ko) which manages bus ✔
    • 212 |
    • created a new device (spi-slave-dev.ko) which manages fs operations ✔
    • 213 |
    • added register and unregister spislave bus, devices and driver ✔
    • 214 |
    • created functions for the registration of other slaves (more generic framework) ✔
    • 215 |
    • matched device and driver after modalias ✔
    • 216 |
    • registered device which child node is located in DTS file ✔
    • 217 |
    • cleaned up code ✔
    • 218 |
    • prepared code for review by the linux maintainers (thanks Michael for this) ✔
    219 |
  24. 220 |
  25. 221 |

    Week 12(09 August - 20 August)

    222 |
    • clean up code✔
    • 223 |
    • latest tests ✔
    • 224 |
    • final raport ✔
    • 225 |
    • added headers with author and licence in the top of each file ✔
    • 226 |
    • corrected all inputs and outputs ✔
    • 227 |
    • BITMAP has been replaced with idr struct ✔
    • 228 |
    • mutexes and spinlocks ✔
    • 229 |
    • documentation, tutorials and examples ✔
    230 |
  26. 231 | 232 |
233 | 234 | 235 |

Final raport

236 |

The project’s initial version involved the creation of SPI driver in slave mode. The first implementation 237 | of realized framework was supposed to be SPI Flash Emulator. To ensure optimal performance, the 238 | driver was meant to use SPI controller, DMA and interrupt.

239 | 240 |

After contacting linux-spi and finding Marek Vašut, a developer who already had experience with 241 | SPI slave drivers, we had a constructive exchange of ideas. Marek introduced me to many 242 | critical elements. Michael noticed a limitation that made it impossible to create a fully generic 243 | slave driver. The project needed a thorough reconstruction.

244 | 245 |

The project consists of three layers. The first and the lowest layer supports the SPI hardware controller. 246 | McSPI controller is located in BeagleBone Black, and the driver was developed exactly to this controller. 247 | The whole implementation is located in spi-mcspi-slave.c[1] file. The controller easily handles small 248 | transfers to 32 bytes with clock to 24 MHz. It supports a variety of word lengths, from 4 bits to 32 bits per word. 249 | DMA was supposed to provide support for longer transfer lengths; however, I was not able to develop it 250 | before the end of GSoC. DMA support will be added after GSoC.

251 | 252 |

The second layer is responsible for communication between the lowest layer and userspace. This layer 253 | consists of a core (bus) which connects the device and driver. It is a bus and a set of functions registering 254 | and managing other SPI slave devices. It was implemented in files spi-slave-core.c [2] and spi-slave-core.h 255 | [3]. Framework uses device tree overlay. On the basis of DTB, it matches the device to the driver. I have 256 | created two files dts for McSPI 0 slave.dts [4] and SPI1_slave.dts [5].

257 | 258 |

A simple user interface, allowing to carry out the transfer has been added in spi-slave-dev.c [6] 259 | and spi-slave-dev.h[7] files. For each installed device, the driver provides file in userspace 260 | (/dev/spislave0 and /dev/spislave1). The driver has the usual fs operations and a number of IOCTL 261 | functions (described in more detail in spi-slave-dev.h [7]), as well as POLL methods used to inform 262 | about the end of transaction.

263 | 264 |

I have added a simple spislave_app.c [8] application, which is using SPI slave framework. 265 | It is a good example on how to use SPI slave.

266 | 267 |

Documentation located on wiki page [9] and in the documentation directory [10] in repository 268 | describes the driver, clarifies and explains its limitations and shows the main problems 269 | because of which a fully generic spi driver in slave mode is difficult to obtain. It also 270 | provides information concerning work plan for each GSoC week. Moreover, it shows 271 | stage by stage how to compile, install and use the framework, as well as the way the 272 | master is connected to the slave on example of BeagleBoard Black.

273 | 274 | 287 | 288 |

What did not work and why:

289 | DMA is the only part of the project which - despite being planned to be involved in the project’s 290 | final version - was not successful. DMA was supposed to provide support for transactions larger 291 | than 32 bytes. Support for DMA has been written, however, I could not merge it. This is the 292 | element that I am going to add after GSoC, for it proved to be too difficult for such limited time. 293 | 294 |

What I have learned during GSoC:

295 |
  • upstream process and interaction with maintainers
  • 296 |
  • how to create a variety of types of modules connected to the linux kernel
  • 297 |
  • handling devices mapped in memory
  • 298 |
  • creating a correct and visually aesthetic code compatible with the linux kernel coding style
  • 299 |
  • planning and describing a project
  • 300 |
  • how bus, device and driver work
  • 301 |
  • character device drivers and fs operations in practice
  • 302 |
  • methods for debugging and fault finding
  • 303 |
  • better git use
  • 304 |
  • and many more ;)
305 | 306 |

What's next:

307 | Currently I am after third review of my code. Greg pointed to many mistakes and provided me with 308 | valuable advices, so my code looks a lot better now. I learned a lot thanks to his help. 309 | As for upstream code for kernel, I am quite skeptical. It is difficult and my code does not necessarily 310 | correspond to the image of SPI slave preferred by linux-spi. Nevertheless, I still wish to continue work 311 | on this code. We will see what happens. 312 | 313 |

A few words in conclusion:

314 | The final report does not end my adventure with linux kernel and BeagleBoard community. These three 315 | months have shown me that writing elements of Linux is not quite that difficult. I have also met a great 316 | BeagleBoard community and I have got to know many wonderful people. I would like to thank Michael for 317 | rescuing me when I got stuck and for such great amount of knowledge he shared with me. I’m especially 318 | thankful for Andrew for his support during the draft preparation. Many thanks to everyone who helped and 319 | supported me during GSoC. You are amazing. 320 | 321 | 322 |

Photos from tests

323 | PIO test 2MHz 324 | PIO test 10MHz 325 | PIO test 16MHz 326 | PIO test 20MHz 327 | 328 |

AM335x and McSPI data sheets

329 | 336 | 337 |

Books about Linux and LKM

338 |
  • LINUX DEVICE DRIVERS, Third Edition, Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman pdf is here[1]
  • 339 |
  • Linux Kernel Development, Third Edition, Robert Love pdf is here[2]
  • 340 |
341 | 342 | [1]https://lwn.net/Kernel/LDD3/ 343 | 344 | [2]http://infoman.teikav.edu.gr/~stpapad/linux_kernel_development_3rd_edition.pdf 345 | 346 | 347 |

Building on x86 platform

348 |
    349 |
  1. Update your linux:
  2. 350 |
    $ sudo apt-get update -qq
    351 |
    $ sudo apt-get install bc 
    352 |
  3. Cross-compiler toolchain - To download the latest version of cross-compiler tool-chain:
  4. 353 |
    $ apt-get install gcc-arm-linux-gnueabi
    354 |
    $ wget -c https://releases.linaro.org/components/toolchain/binaries/5.3-2016.02/arm-linux-gnueabihf/gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf.tar.xz
    355 |
    $ tar xf gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf.tar.xz
    356 |
    $ export CC=`pwd`/gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
    357 |
  5. Cloning SPI slave repository:
  6. 358 |
    $ git clone git@github.com:pmezydlo/SPI_slave_driver_implementation.git
    359 |
    $ cd SPI_slave_driver_implementation/
    360 |
    $ git checkout
    361 |
  7. Check kernel version on your BB board (connect with your board):
  8. 362 |
    $ ssh root@192.168.7.2
    363 |
    $ uname -r
    364 |
    4.4.8-ti-r22
    365 |
  9. BBB clone kernel source:
  10. 366 |
    $ export SOURCE_BRANCH="4.4.8"
    367 |
    $ export SOURCE_VERSION="ti-r22"
    368 |
    $ export SOURCE_REPO="linux-stable-rcn-ee"
    369 |
    $ export SOURCE_LOCATION="https://github.com/RobertCNelson"
    370 |
    $ wget "$SOURCE_LOCATION/$SOURCE_REPO/archive/$SOURCE_BRANCH-$SOURCE_VERSION.tar.gz"
    371 |
    $ tar xf $SOURCE_BRANCH-$SOURCE_VERSION.tar.gz
    372 |
    $ export DST_KERNEL=$PWD/$SOURCE_REPO-$SOURCE_BRANCH-$SOURCE_VERSION
    373 |
  11. Cross compiling the kernel source:
  12. 374 |
    $ cd $DST_KERNEL
    375 |
    $ make -j3 mrproper ARCH=arm CROSS_COMPILE=$(CC) LOCALVERSION=-$SOURCE_VERSION
    376 |
    $ wget -c "http://rcn-ee.net/deb/jessie-armhf/v$SOURCE_BRANCH-$SOURCE_VERSION/defconfig" -O .config
    377 |
    $ make -j3 modules ARCH=arm CROSS_COMPILE=$(CC) LOCALVERSION=-$SOURCE_VERSION 2>&1
    378 |
  13. Cross compile SPI slave driver:
  14. 379 |
    make KDIR=$DST_KERNEL ARCH=arm CROSS_COMPILE=$(CC) LOCALVERSION=-$SOURCE_VERSION
    380 |
  15. Check the driver version:
  16. 381 |
    $ modinfo driver/spi-mcspi-slave.ko
    382 |
    filename:       /home/pmezydlo/BeagleBoard_work/SPI_slave_driver_implementation/driver/spi-mcspi-slave.ko
    383 |
    version:        1.0
    384 |
    description:    SPI slave for McSPI controller.
    385 |
    author:         Patryk Mezydlo, 
    386 |
    license:        GPL v2
    387 |
    srcversion:     469EA334B14612EBD6F0463
    388 |
    alias:          of:N*T*Cti,omap4-mcspi*
    389 |
    depends:        
    390 |
    vermagic:       4.4.8-ti-r22 SMP mod_unload modversions ARMv7 thumb2 p2v8 
    391 |
392 | 393 |

Prepare BB board

394 |
    395 |

    Disabling the BeagleBone black hdmi cape

    396 |
      397 |
    1. Mount the FAT partition:
    2. 398 |
      $ mount /dev/mmcblk0p1  /mnt/card
      399 |
    3. Edit the uEnv.txt on the mounted partition:
    4. 400 |
      $ nano /mnt/card/uEnv.txt
      401 |
    5. To disable the HDMI Cape, change the contents of uEnv.txt to:
    6. 402 |
      $ optargs=quiet capemgr.disable_partno=BB-BONELT-HDMI,BB-BONELT-HDMIN
      403 |
    7. Save the file:
    8. 404 |
      Ctrl-X, Y
      405 |
    9. Unmount the partition:
    10. 406 |
      $ umount /mnt/card
      407 |
    11. Reboot the board:
    12. 408 |
      $ shutdown -r now
      409 |
    13. Wait about 10 seconds and reconnect to the BeagleBone Black through SSH. To see what capes are enabled:
    14. 410 |
      $ cat /sys/devices/bone_capemgr.*/slots
      411 |
    412 |
413 |
    414 |

    Disabling the spi master driver(spi_omap2_mcspi)

    415 |
      416 |
    1. Create a file:
    2. 417 |
      $ nano /etc/modprobe.d/spi_omap2_mcspi.conf
      418 |
    3. Edit the spi_omap2_mcspi.conf:
    4. 419 |
      blacklist spi_omap2_mcspi
      420 |
    5. Save the file:
    6. 421 |
      Ctrl-X, Y
      422 |
    7. Reboot the board:
    8. 423 |
      $ shutdown -r now
      424 |
    425 | 426 |

    Installing the Device Tree Overlays(DTS)

    427 |
      428 |
    1. Installing the DTS compiler:
    2. 429 |
      $ wget -c https://raw.githubusercontent.com/RobertCNelson/tools/master/pkgs/dtc.sh
      430 |
      $ chmod +x dtc.sh
      431 |
      $ ./dtc.sh
      432 |
    3. Compiling the SPI slave dts file:
    4. 433 |
      $ cd DTS/
      434 |
      dtc -O dtb -o SPI0_slave-00A0.dtbo -b 0 -@ SPI0_slave.dts
      435 |
    436 | 437 |

    Upload spi slave driver, dtb and slave application on BB board:

    438 |
      439 |
    1. Upload using SCP:
    2. 440 |
      $ scp driver/*.ko  root@192.168.7.2:/root
      441 |
      $ scp slave_app/slave_app  root@192.168.7.2:/root
      442 |
      $ scp DTS/SPI0_slave-00A0.dtbo  root@192.168.7.2:/lib/firmware
      443 |
    444 |
445 | 446 |

Installing driver on BB platform

447 |
    448 |

    Installing DTS:

    449 |
    1. Make an entry:
    2. 450 |
      echo SPI0_slave>/sys/devices/platform/bone_capemgr/slots
      451 |
    3. Checking:
    4. 452 |
      $ cat /sys/devices/platform/bone_capemgr/slots
      453 |
       0: PF----  -1
      454 |
       1: PF----  -1
      455 |
       2: PF----  -1
      456 |
       3: PF----  -1
      457 |
       5: P-O-L-   0 Override Board Name,00A0,Override Manuf,SPI0_slave
      458 |
      $ cat /proc/interrupt
      459 |
      172:          3      INTC  65 Level     spi-mcspi-slave
      460 | 461 |
    462 |
463 |
    464 |

    Installing driver:

    465 |
    1. Command insmod:
    2. 466 |
      insmod spi-slave-core.ko 
      467 |
      insmod spi-mcspi-dev.ko 
      468 |
      insmod spi-mcspi-slave.ko 
      469 |
    3. Checking:
    4. 470 |
      $ lsmod
      471 |
      Module                  Size  Used by
      472 |
      spi_mcspi_slave         6562  0 
      473 |
      spi_slave_dev           4307  0 
      474 |
      spi_slave_core          6046  2 spi_slave_dev,spi_mcspi_slave
      475 | 476 |
    477 | 478 | 479 | 480 |

    First data transfer

    481 |
      482 |
    1. Run SPI slave application:
    2. 483 |
      ./slave_app --w --r -d /dev/spislave0
      484 |
    3. Application and driver waiting for the data, connect the master and make your first transfer!!!
    4. 485 |
    486 | pio console 487 | 488 | 489 | 490 | 491 |

    Testing and wiring

    492 |

    The first option:

    493 |

    One AM335x processor contains two McSPI controllers, 494 | what allows to use one controller as slave and the 495 | other as master. This allows to carry out tests on one board.

    496 | one beaglebone 497 |

    The second option:

    498 |

    The second option is to use two boards where one works as master and the other as slave.

    499 | two beaglebones 500 | 501 | 502 | -------------------------------------------------------------------------------- /wiki/gsoc-basic-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/gsoc-basic-logo.png -------------------------------------------------------------------------------- /wiki/how_pio_works.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/how_pio_works.png -------------------------------------------------------------------------------- /wiki/logo-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/logo-linux.png -------------------------------------------------------------------------------- /wiki/one_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/one_bb.png -------------------------------------------------------------------------------- /wiki/pio_10MHz_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/pio_10MHz_img.png -------------------------------------------------------------------------------- /wiki/pio_16MHz_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/pio_16MHz_img.png -------------------------------------------------------------------------------- /wiki/pio_25MHz_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/pio_25MHz_img.png -------------------------------------------------------------------------------- /wiki/pio_2MHz_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/pio_2MHz_img.png -------------------------------------------------------------------------------- /wiki/pio_con.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/pio_con.png -------------------------------------------------------------------------------- /wiki/top_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/top_img.png -------------------------------------------------------------------------------- /wiki/two_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmezydlo/SPI_slave_driver_implementation/dfb139128d4c5196d6f1ed0dcad5139b2b68545f/wiki/two_bb.png --------------------------------------------------------------------------------