From 11e88380d531854bc7f07dc61a2f48dcf70ddd22 Mon Sep 17 00:00:00 2001 From: Frodo Looijaard Date: Mon, 2 Nov 1998 20:29:28 +0000 Subject: [PATCH] The basic directory structure for lmsensors2. README.directories contains a description of the directories I created; read it if you have questions about them. doc/design is the design document I sent before through email. doc/useful_addresses.html contains links to I2C, SMBus and sensors information. Note that we may have to change minor i2c things, like Makefiles; this is why I put it in the archive too (we should, of course, send important changes to Simon Vogl). git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@3 7894878c-1315-0410-8ee3-d5d059ff63e0 --- README.directories | 14 + archive/mod-19981006.tar.gz | Bin 0 -> 36228 bytes doc/design | 497 +++++++++++++++++++++++++++ doc/useful_addresses.html | 60 ++++ i2c/Makefile | 40 +++ i2c/README | 22 ++ i2c/README.bitmb | 36 ++ i2c/algo-bit.c | 665 ++++++++++++++++++++++++++++++++++++ i2c/algo-bit.h | 38 +++ i2c/bit-elv.c | 146 ++++++++ i2c/bit-lp.c | 188 ++++++++++ i2c/bit-mb.c | 163 +++++++++ i2c/bit-velle.c | 195 +++++++++++ i2c/detect/Makefile | 32 ++ i2c/detect/detect.c | 68 ++++ i2c/drivers/Makefile | 23 ++ i2c/drivers/eeprom.c | 172 ++++++++++ i2c/eeprom/Makefile | 41 +++ i2c/eeprom/README | 4 + i2c/eeprom/a | 16 + i2c/eeprom/b | 16 + i2c/eeprom/eeprom.c | 187 ++++++++++ i2c/hw-monitor.c | 182 ++++++++++ i2c/i2c-core.c | 467 +++++++++++++++++++++++++ i2c/i2c-dev.c | 364 ++++++++++++++++++++ i2c/i2c.h | 371 ++++++++++++++++++++ i2c/old-code/algo-pcf.c | 291 ++++++++++++++++ i2c/old-code/bit-lp-multi.c | 189 ++++++++++ i2c/old-code/pcf-elektor.c | 115 +++++++ i2c/old-code/pcf-isa.c | 110 ++++++ i2c/old-code/pcf-lp.c | 228 +++++++++++++ i2c/old-code/pcf8584.h | 72 ++++ 32 files changed, 5012 insertions(+) create mode 100644 README.directories create mode 100644 archive/mod-19981006.tar.gz create mode 100644 doc/design create mode 100644 doc/useful_addresses.html create mode 100644 i2c/Makefile create mode 100644 i2c/README create mode 100644 i2c/README.bitmb create mode 100644 i2c/algo-bit.c create mode 100644 i2c/algo-bit.h create mode 100644 i2c/bit-elv.c create mode 100644 i2c/bit-lp.c create mode 100644 i2c/bit-mb.c create mode 100644 i2c/bit-velle.c create mode 100644 i2c/detect/Makefile create mode 100644 i2c/detect/detect.c create mode 100644 i2c/drivers/Makefile create mode 100644 i2c/drivers/eeprom.c create mode 100644 i2c/eeprom/Makefile create mode 100644 i2c/eeprom/README create mode 100644 i2c/eeprom/a create mode 100644 i2c/eeprom/b create mode 100644 i2c/eeprom/eeprom.c create mode 100644 i2c/hw-monitor.c create mode 100644 i2c/i2c-core.c create mode 100644 i2c/i2c-dev.c create mode 100644 i2c/i2c.h create mode 100644 i2c/old-code/algo-pcf.c create mode 100644 i2c/old-code/bit-lp-multi.c create mode 100644 i2c/old-code/pcf-elektor.c create mode 100644 i2c/old-code/pcf-isa.c create mode 100644 i2c/old-code/pcf-lp.c create mode 100644 i2c/old-code/pcf8584.h diff --git a/README.directories b/README.directories new file mode 100644 index 00000000..0654fad0 --- /dev/null +++ b/README.directories @@ -0,0 +1,14 @@ +The directories within this package: + +* CVS + The CVS directories. Do not touch these! +* archive + Packages from other sources. Currently, it contains Simon Vogl's i2c code + (mod-????????.tar.gz). +* doc + Documentation about the modules within this package +* i2c + Simon Vogl's i2c module, including any necessary patches, excluding any + files which are removed by 'make clean'. +* src + Our own code. diff --git a/archive/mod-19981006.tar.gz b/archive/mod-19981006.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..33152d7f897aff408bce5daceda981e67fb13539 GIT binary patch literal 36228 zcmV)5K*_%!iwFSd$r&~P1ME9nj2qW=vzB6uQ^!hDw`t=9xLU0ga+jPXxlb)cHPn(T zv)x@1Bv+3}8+vx;lAPV)3_FkAWgI0nRHHTw7yjx;KidW^(4g&C0Jmsh)JFZ;plJX4 zQ6oSRApNKt)F@KJ>N)q`A%~A$Nr9vwi3Zx4`#ksDbIN=L?VRD zEzAmh#gimtaWT0#H$T5LpMv^QYJN^43y&Hxe0HmsWGNVrerw>$}dZ>u9s4Vflyh>Q<#v*sNuzqHc?tCh;w`O-aIu$?{LLsB6RQ zw%Rl_(y$%kyN1;yY$dWs9gCX8rJe_kE(|_sQ47#)cMKDU0h9YCjk>V(Mk!yfZe6bD zi$xkqiNrO_PWYDZQeBo?)S)YK1ooGG zsdJ#elp#?~CsWa((~4z)n0u4}Eszx1sVVpfLLr?D3*^Ey0g~&JghnQ{4oU0-9FAuU zOV>pa4t!@;Or4#Iii3~MwhC99lx5ANss(@&PMahe<3T9XimfzEwds=SZ_2XYf$@L> z018tMiglgt_|0sx*BzoJyz3ryy)2lJ{?Jr!XrHL6OS8$t^wRRcD*PmZ7Qq^`ly)4u z3bQz9S=Q|Xu^k-7yL(jur&s4|_NoE41LJXbNO8^dbvov#W-CZp1AA)E zC-(nwJl`s;uT4x$^y(AR327qKfBhu?&a-z^`m`iVul#2ohHPECT7Ba?Q`e#RkNK&o za^wB{)Okt5Pjo_pcN}Lx*Ke?{FHEqmFG>?{)~0H&*QUrD(DizLDhb~&!gn6pmf!rw z)WtWwsi}|Oei|nK_}ei3^U_x9DX9GLj{MacV&3pdGFb`Ui(rMO1l$p z=BHjLx1?Ycblv&iiPNwCD>PJF`F``MJ6X1))4zWQ-ml%7x(I(S?mh+czf#`n&wa}K zY_+u=n)jotZ>9eE^^Z<}H~;+2GQPjY-YR?R?A>JVUG{FXcc)xweG7o#76qW_8?~v= z-1&!7ul(dLbbRx#cR#fobe($j9shLdots~)Z@=;$lKS1(KYIH)oc{jz5a*A7{Oz(B zdUEy7|6M!%ho5|M_wK-ix2kX4nmSc&{cV7GmAzB%z@~1#`{up`L_Yl_1j0!gGKI6jA_T`l=xzQ zGNmL_@a4W=9rc%CTiLXdQc~%()KQ%__XP|XbYSf#xN=(|HK&U%p$C4L=c!tYm2|b^ zQHPv!i7c(#_8yKvFO#F`%2F`f^)-#UE;%r~7CCp=@4&T)Lsf7H90z9d0s0wPI+xa0 z$hou0^x>E{NsuG^@_O@mq=@W+g(p9tWA z>NpI|3*e=O;KhMbM8G={f|n8B0N!_=8pJ<<_Z9eiCe)7Oo)6W7Ev<#h_`Cz-&W;&Z z66IjIA3))gG2^ZZ!TaFE_;o)U+5-c&r5JH--pI$p^VNa?)Nhir(GCc z5{UGC2$7>?INW|x`tWYJ@5Ioo69X$?{ZrDQ13WzbnW6frq59K9^@#MFqWpOYY4i-^ z3yvGsPe_ZRye#TD_+Au$Sl<-m{}fR99N=>+1Rv?73f0}A`tJ(WUmL3bG5q}*!2XiN z;s3=@|6jr1d(i&{(f{{iFVBkod>_95KEcOg|Gx^&{}KHC7xX_T`hPOiADQ6$=kWgf zu243LCN(uJUFR`2)fK5;=Mi9iP$NsI7{#PoyJL!Cp1GW&QsNtkw?V+btZ>ugS z6jTd*(qhEC?ETD~GA; zb?y&KEN=BX;a(j!riW3_quxW$V**&u5#Iy&K6P4>z9H(bi25I39SHeH;`=A!`{&pX z#C}_h|DG8CKGs1Y{Q&DAz#oeKk3|3C1h40}4FRvB62F4oP&34yf~nWhY=3wVGZl;ZWVzps_u_ya;dAG@4ezwC#?YP50`% zetV$a@GZ@QNP)HGSIb-31)QDVDCM`-3-_gL<6vU|^A4yf*WYnH_%fG27GHrbuD4Ok zh4nbrd8r<-eGDFXt+-yO)_KQPxn3*P^SN4q%=B%A3_*#}_ytjVWAgtGozL&#|1B-d z&GqF!y}KK@~Q%%9-;__1_)3fr~zz4XZq0Q=JIf1naobp zLqN<0nkfLfH*lk{Q7wM80Q|hnSsgY6U8-+-0ioNpZ984q;@2r+1p_e8z*8E;(BW+~ z4A8?WL5J6*Fn`|w9qYAJ=y!2{7S%kevplv%U~eP>AjppIDr}>m8GX&;7vk(xNjL?Q zk%Iv*KMYYff&*@61g{FDt)iiA1$azjAtu9)M)y3;XOB>A$8X$BFD!0n z1}3##2w&W$dvlvpDDrZc5+oTXsW_Rum7I)|+wqa^VtTc{QLp6V1R%$82|f&V5~|wLj)~55M2;luLYuu zivbTRgkgaq*CSUz?}m!WRYtXQha`GVk0FVTH~^1j*t7^J0N`Ms;<~rtZy3bTjTM{U zLlTvyK#?(7a(jRTyfHP@*e6Vom5t(g#TlOhr!nXO3dor(NfWWg2y#c=njw~Kf^ZQy z#u3mbCY&&bh0MCknmM2B0^9Ch1U}E~&SaNktfRN!Pkjm3!z8jBM+x1XNp6cmDk!9b z!rZnP%d7K2`$AAy3<^uzVn^cC1Y>52$Ztnc-P(=li28`d$(6Nook!!na}1_u3F@$z zI3?e)2#R+E^s(0IicxVA=3#Q=LLB&Tnq2_FPQbgr$n62m$dmA+K*_WKj;e8-f;sY* z6VHLq?I{;I!jk+^0TM78LMSl9>!n=2&eaAbyGv}T{0WCDIRIbTqdFT>4DRDJ&^ijnJ48GRZ<7D49bTDyFlUq0tS~zRy;Zm-t?f}VG zpq0}cxH4pPxC9$et0<`!Jvgnq~Mibt8bQS#kCvpz%LTYw_oSluwNHA z#hG&oVg~ylJaZ!RPraP0Y%oI~pEQuF5MvasmrIoztW~ex*tlF;uc9x(v_j}QG>T%* zBpkGhLz@PQLGY_sR59piiAV!{nYLjm#O=bMwjxI$3aPayC<0WAmdy`__#HT5z9S4b ziQNdiN0*#ulYk!>DK$73aNjI?5P0A`;!zzs81k@L9$P8^1%4g2f_F4{p&K2D2k__y z&W&lc!8_kR2?j5JM!b^lU!U_ z#2dlX{FD68V|jpK6A$YV2@DniGInC>K`23FG6&rIw#&j9{9U};G8$e_Y=yJWVLWV{ z2wAl|UB_s)JQ7`vk<^6?3yBL$q{>oDR}^y9Zkn=;2-Gk(1LZv zd@`hK`$SVM(1^N$N#`9O^pxRYVqq5cg};ewbQvIM^evq_A|iEQ=b-Sga%FRiT)~)B zHA&guF$#^WLu6>VEMVEvW8ex1r}t@2FT1{W(WnM!Xo1LHV|xyMFhk+ zu^k2_3hJ|qsSmq@6JvmJmtc_Go2!f`K!2Y))6W3AWp{!Ulm|hZX_6gEAoyGp=b_2FS){3>wLbXcPN)?hLVAyKK)vfhhg_O4{sJevYO%CgUoGVe%j_r64ltyo5}@lO8iDJv^vd%hkq=o5OMFN-;+qV)1<@ zX^aP5U_&VHtENxgLA1b?u9e1syTFzu5te+17AAGltH3 z1T>uNf?Fge56Qy;aOIa>XtkIWec3?+$F5ZPb)E1z&;jTo^MgsYT<&NbB#_-9eAGfK zH_)YlSxjJ1jglxBCLIcBjNtDU25!C(x8B7}P;qZwF4pR~e6C#I$X(y&B)|@csC~=J z$m4Exg38EbajwBn(3q-%Gqt&gg8{Pe2Ee6YLI=VG<5q@GJrce>1YjWhJhu+|F2)_} zOQGh(CDbx;)P}KvX_`59nrI$7t=R_~Olc0UG&ov73y&UHfciK;Tmk1YR@m6Z?Sn$74Szyf{u2h4kJIM5Ce_ zU^vhKATn`wfvMY|$*I=6QDZ<|VG7_M7!UAIRHw^&QBSeEd~pFK?uEtfn1$8Md(e(w z3PYsq_0*&+N5b1pv5frq%`ycU=91v1g2d$TO$CWC-Akfp1{o|yUXDO#aNFBDAefK? zg#xx1ETq%B><5Q$kJAZ-Jyid6+#}hlVj}K=s=!Hy)8!qcP>bt z;yiF*&H=^57FbSJloM>Xu@OOt3`#*bLq~e%lh`ccRsBQ5G#HonEW`bqPbSr<&>1F2 zGd~d-P>8;$^G4=jXWDZ=gY8$zB+L7At;Zl?jcE~43v!N#ghM;`iAQgJ zWJvVgc8)`XRGk2kANa;4$(AoOHeba4xs6XNE?+f)2g5#_AG^8%DEDUw<-E0HWo zNxRi5FbI4kumS-X07cT#I!@(3yWMQE>EBG6>GRrsGwr0W?R3)AeMwuVcH^`!dG(}u zGoGp2=V_C9^0V9LG#zIXyVg6ubMF1V`~3k(X(_Gkf1<yF73P5j2i0EE^=Yd95K->U=@X;g}O zTOB*54*B_0fda`54Stq&WLK(3*&m* zg;q8~k1@40J!v#kG~t^YHQ0r)90~!}U}>DvPS`DEZkz#tWIz`sIe}Tk6N+s}Cf*Cn zOpm1qcW5~Q(y}4Nt9|_EIiz2 zb9ksiWXD|>7{+)!9+;lS(x!CHDNEy`Csz`^0wpa~5C&ceZQzB*SL@y9v`dY7fSA%; zuNGOc1#u+pr#mM=7>O)>V8f}GoVck@2}}$M#+t)C2OIp=^=eQoXW}d%xfFwmNELd) z;vEnR-H?o?vLW8A_ie?0a~C|^whQD z+o3Px@gP|W6`Q~N-ki9b-ZZs;6FU=?=`yGh$mk=Mp@Ih9Ff#78DLWve#k=pK|8_fO zy^)b>bqC0p2vjmu=-?Yh#XIZEZm4)4yAqXghjdqc7zrPCjo8xAi$D7<6wijc%uGIzTMc%VM0)?XZT3zFhlY$mVRg*uB?D zNN;zvBMCb!u-z$?^4!fLOoJh|(!t&zQ8PDI$6YUm>&2SB*=%ukacj^TMt-7BtE@I! zrQ8ff3%NcH+CyRVA#dJ~@g6?tiz|dDw7I&)q9{u+k4u$8qh7LyoD!VYHHvcz`}?8f zh{sX2o-5mSsgtE2(Rv`^?tr3zigj#6}knBhWC6ch$OwcCE5OJL)+!k8j&zxGTIh zs8Sb|FyY{y}2?$WiN?F*LW(F{XM>?xrKI5;IR1Imrbot8!wa8c`{KWUZFM*U`4WG#uicD5_n zsg>9g6L1#lKOOBqhy}!>hb;YW%8X0JP^4k>RYm zK~O?KE=(>xt&DEdBm2mg9n))?yQ1;7CR>6Gzj_byQ z=^uv0Y2zX0+Y9k#3bm(kt+<`0MQu}w!wIlew*GX47c+Bt#4SbA9gIhKK<;!zqML2p zybLwc4uP$8qVOZuz{G8oO)7UZpsUX*cTB>aXSX{y{>E^y+rUM+dx0=)!2z(F4=nvK zY;>ekt8jPqjdj~uHbidF8{v0-Dg8st3dTczvwcnaJ8p62;QzvGu*IiSMLZrIxvAu1 zTvPFRk#6i7Zwut*gbp zndxe37qhPE(cFlsXKSZ0t*c*4Y8Rc5o*pdz}|#_&l%iT+$q>Lha`#0u-707~)30`K(I z)*1yEIJFwJo^wV89PQcf&V9o6#m4_GG%3YlreIulcJRXNQ44GqYL`p>Wc&)xQCl+| zs3@k0abMMqicDuYe0FR5M*X{{^)_eS!wBRTNqs8vMxod@2YpSp-@qtvO%+W;P>-6!72h#wwh!|soH z=r@}ry2_rvmO~8j6)l)2tvkwRpDCzR?!6u4>7Lk*AQGj`3NTQXJMhHgs;W*Y+mumU zKsD`S1FeY(w@9oumO@H!+0Ko`RSSEeo4YV`!Jxe{Fg5ZmbNAp<%#N2!QT3WhGL>&l z)0#d*ZS}nC?lG^MA99&D_Td5`H0i8r+fDbvQsArLiN{@*m&&FaT}0GlZ5JV&a!>)9 z(tEltEr92v8NLpme4>qcNy-ql!Md~Fb~L~~%A*LQYBwR)#C<8%J5tP@eUDa^B&XlgX+C*27NgmDS zw^E1G)zhbQDN!zJ*2|YHb68&5Aw{&56BGn|%=sVmw;!Q*GpN46uqS;W#xyy=7QKmnrOx$YLm3*UOl^|2u@BP@>b zyl}lnK~vxlB#s$X4*$h_gn_a=aJn)}plEFG$bLiGpxJXyh|F(QSiGic&UL4z`c;MBS^ef%YcNyE5hWh zae%lYdw8)oVgj4R+PX?hrTuU{$0i`ViM5-6&ce_85{W5sP^eL*0D84Sy#1yZ!@JE@ zAz_KREbWbsxNRbGM;_FPACPQHglwL%b9l&{mNJhdy6Bg)518=-qjc6%*uDKeYg-W#s zx@=X?JN$8%FfJi63Q=7>eKMOBdAlF7alg7WJE^8X_z|ay9Vk{B#a6lfgj%|Q<*)xV zuPj0fGL&z9C4te`&KA1+)5>W#Gu}a$C5;x=Ad~n^$Ux(qQD>#w5>#{7i;n=c)8Yv0 z*)zACT2WkDl~ZhQ(^!3AqguL_NhBr_{j-UpcoIfc^tXoZ9t!75-(%_`f4o{Fi)w5cGd!X#f1*`*PibSSv0% z@!_U4_w_tvx}}L+yR)tRl0#Iiy&nf=KMu@(9GLI?I55E&Fz&qP5HLA61Wa~FdeJCV=Q z;xd-ajpaQ6eSLk(S#J`?)f&WS!gCt-a(#VaNO^`daw0o?B1csj9Lwj%21a%ZNvu$J zAM~bcXq%|mF)*8}Muv?E$CC+1jRBXuh?$X5&wdSkwaKs zsx@d*9xF-MlwdfSI0sGL)S3u8DhtAKNNARLAma>5-9k<=ZPbI(R`GZ+>FnGrN2A#i zg>=UePadHBM%&FO+1wtgH4u*GP92baJ*2l$phsIuL2H@wBG)o>G0$=zLPJ4wrJ!Bu zL7@y== zD=8&$NK;An)(S;p;jaS6n;-^W0JGA^-x0{HIIWnRaD)!gYl`nZ-HEQ)U;43$?A9uR zYg_gSfh0E&K?Mp<*2@Q8rq%T1@}%gZx>T-Qd_$Eh?@+n?ZYrlt?Tl5AhhSdiY;yzc zTabh)=}MuQgztYU#XfY|QN?DsFLZ-xH+YvY2eUn3x+*_ERE~E5&ph6-ZPb$?+*v7t zFQQJ1euc_pw+2>9srRV?HKs;W*sD>l1;4!k5Ks7{lZy%-^tc@I{`@6oZ-uTI6WkSP zAvbW;wI4lmap_r$M#AwT?%Jd>jj|N}fIOy1e%~g(i9I5Xt7p{4sjrHR5HwnY>8a(# z^WrJIbbe}C6*pD^D6UJ@P6C5jZ(~VWbw}lMSvSJFfm+f~`C-?JFF7@$vRrkJ!{OCE zqVjIA^cFm6+nt?eAA*o87V|U~= z;In4xrDC(a!6!VhOYTa8m~?Lu*3wC-(P{y`h_?{8h_*ID+-R}GtsC!!9dpajNbdXS*4ys*G@5O(pz$wJY5#+A z(R#2JOtBVBVTfSh5%2&uDUWwKw94pzpr^c7I)>i*pImO(JO3#=IDqq?vctLjzW(Qa zT=z)-19aL~|Lm)O_SHXcDfJJgJ!^l4b`=14ud8U&=Y=Cze27wTW`kvng(2#nmo1;U*LKq1m4+re?zoY^&l1IqkdJf7XkXE{$U zZBHcU0i2lM;7 zv!hav5moB6YLcEXJMIh1s^QPhc?INhTyd-%aYT@bg3swq0YhGThsQo##55ceDa**J z@9~s=ygK4WL!82fd%cR-Qtp@;g9YEL8uP0>L6PD~+s!1E^hbGxs2JyDTfQIhHa3dX?LQt+`*4($rd zC5p_!A#<(If38c1_@{c9x4h+8I?J7af(WFbq{g&k8$*?IcJ=uLZj} zsQNo(RDYi0&z(E2_Ada?*OuJ^#N>?v$T~#N-ZiK`WVKTAP8EZ`t_oY$hE3P7}^gZTIdwChV zw1p@%gl+t}%Y`$A^9$x!-h?0D{P(U7%bmaed;2{KpLfHfNw=ffw8AHs&8C6=am03p zL75kIq3cO#du%Xndd%nLV|puc(uS?cSyQbo!o#Ps^|E{1)l{GQjs3s)ss z$7mp7z3Ls;d-U-wcI82JLJIhuEzE7dm_YSxGAi`YGdaI6`akG@8?|yjY$i|KqcYTB zc4TCztNkBcS=Rp!4-950DeHe>|2=e%>f^iK;`(3T_FN_Z9y;Orc$!|HmuGE{#9GTh9LbE-CnP07n`;RUpT!cBgFusdp3jNAm`=r7< zJd+S{iuU{%x$eir=}B**(%49-?&S3=s?69Lg__~v$K0p@UMTBPS_#R_D4?jK~h$&Pq%0cq(TE3m4 z3HONqG|@HsQ>b|p5eg4Sr_&K{(p7z8O5m^+H?D9fDw&R6YsXZ!1KI|A1mTQ&^+=b9 zws#o{E7TcQD6F2{;frr(qJo-YOkmK6c%T#6bTw_Fmn!MXh!_}NSXJGZ$##n4&q+`g z3PAY+Rl7j7%_46c*P4~0%U!c~>H10)^=8t%UhPC-B@*=*1C5XzO^*h`Mp8xt##Cu> z9f@?9M1Ci~%hi?~I(VY*yK>~{w;h&y4@c3&$94nUf+1Fx(oO{@%{`CGXO(qdg7e+& z$bN4G=i9^9e>Vl^aO=25tfT%{}a=xCt><{!!mrjMIa0x zFd`6!uLs4tBXZ=1;p<6{RG1)j55u?jQTPHxUi_Z{gnJmEAN%{i_v5+;d#%8tF8*JxP}0CrE#+z}<2RJLx0}Gc zxwgL#yuS~;zYl!3v#6H?m~ZvCix@z`{Cge;DAzj<(9Qs?@IToBxV2@oW4WvgV71z6 zciaic(*j#z?QVdEPvp2J)DHSTINSvgaCCNQ60C5hTca3MZbapBs1sGLccb3yNJP61 zMf6tk!m`qyyX@^o6D))gk)qsvJlQp7N0>EC$NCxviWYw-2LT&u0DfC-N)6&)N%+gLYSs>-7xmd`^_8UCiO?Lk4#6y$L^_;h z*=UsJH1fA-#rG2Nuao!GRs)^A(mX8&cx-TCG~ALT3zqwWtgq10hT3bF-8g*9yc0i?EhmL= zeEt+(fXogebS2>=2D+U*#J^C)3bsnmjf=3y;YW{69O6gMnbSH7%_Lr=br0oJDL%7< zVWQ8Qw2CUy8ZK>g$Ic`|1Aau8H{u4M#Y;ZugsL)20`iQ6ZMCYiNYdSzs#~9= zzc%@7s5bOyhuRQ>tPi20Ipdp*)jA4$GDVG6IKv~Sj(o%aeND z78cw99chk~;I7=A)`kc(fRalwh~?P}Gmu@d04A3f779ztij%ehP@47BOfbmIWd-Ss=okm&$Zj3Y4zhETQPIXNy(GIAf}C@i zd@`2LY$)qfWac9EySudi!c3rtP#$;bF6pXq+u}>l?LlAUfH!Ezbq5vk^xJktc4@BV zax`hD)FQ4%!q@mhl^SQIHglM}SBvldaI{^Tc#RqpFOlbr2Af?4jWQQgg*u1?+g8~+ zveEPXO{tAwowpiR(6&@(z;Qd34lqK0+@W z7$x8CMcm!KNJ|k%AKw~7sTt|Kz*dGjEMANoZ3q~wRQCpgBmTLY9ldrtb~vMsnMib! z94t7Hf*U^bJLoU!uCude=SAXvpEAsVAw2vJ?u7gvN6U>zoh%oZqvfn4@dgIdJB+b+ zf?uzMQrUqamEyXtAO=-7o#Tx$^jNUMZUR)gRX%|H@(T*9PRMIQL*ZgTRr2q;))VGXy6Gm7#Ca zmvTNK7Tom)iQ~Hd%uLH<(;@HOu?beK+WG*o7+Dz#IdlQUIAaFE3a0NX_6C^BR9Xe< z;^kllABn8<4tZek*^!ugBcOLyff{Y4U4P9=Ch5~TUGN=1)yH1+f|?bm{&36zyqLq8 zH0XQA3AIG*({=zgqJjkYaI=aiUu=I2uX?Oa>)WzB?C#Tv=l4Cd;T=`GKj!ebGI&gg zyuY=%Z~FVac?I`>u{)Qk>}?r4*#8a=3g&|Qzqyfp|KIy^?GZ`S+e7aDu3s-M8koJ? zLF#VUyKmOJzx(<R6geH*MJC-Ns%c62Oza%?E)ZLs?GR^4n}H&^{o zdT;spqS;Ob&(LCt=uVnvRDg(yvM@WRSbyYuI2cG5?tCjsv=Vle} zFZiA=El={(!N5~4Et~f}ot%1_pXRK#FBt*gZkn!#HnR1MzHMPHZLKpB%~Dg)7veCO z)7Z@HISN(vgEUP7ow~HV#Qg+yu>JhZ%wo8wCa1IEjyk`1Cj4-6aw*(-=O@pE`|iw{ zP}faC(Os%F5cifP)OxA9`yId_+Dx5)dH{U}Jx%b%;NU<6LePt0$&nijz8KXnMuRVM zd3jN?8loeLwq<|)PMZ>J$nxUc(n%hGEDx`un``Gj7#vX~8ypg?3MZqRF3EB^9uG-I zH{Fskv;o1Xj0??b=%hZZ6Oteyd%ak*^p3?6Njzl^e%(+sG1HiaVPprT-BwsS*Ne@j z9GVK^>slOR|1pc8Rw|`D{-b!E76+F8Sx5poz{zB4%w zaZMtEBkR4&edh-Z^6~$2gm3@)My*rbLo3i7>_79l!QlBX1A_zm{(tx7x(8;K`{%#! z%MAN6!&}Z`)4zD^+HUhrI|2TVj0bl(oMneI<9B^%ymvP9>~i5eM|6x2NOa|`jH++2 z^%-HGHqmgHh2m2m?YPkTP(fydeku^MoTI+*UW$FFyykomH`#xhg$hdlmY|Ye1+R)4 z_@}bz7t+D67rGpp!7<<8N=B4!TlY^`f2*Ca?wdB&4%1uYlyyD-BMw^+SjFso2Kf#r zp?5p~yh{qo#XGOTU7mt&(Ln!U=)rBwJ39+K8m!%}yE+j)43@*`?%*xk3e8~x()Hz? z9HidUh^TAnY=N-*@@?1IUgxg+@yP7Y2j0*CTW_p$hV`~OCE#;Sa1j{%bT`%0engRu zPy2^i9){vHN8mf3FKiEBJmRqSZhgdkvimNK++**W+uLRzeT(d}@5!0#U5-=_9=RSo zQayO&y569-uq5L2bekT~kXfwX@fPnV*2R~1kRWFQ39d@4UKdcG`{bFf(adz&h zq?a>g!iAsci--tr9rPU;G2A+7o8FsKJfcE|>%C{fwabmxH8xMJ5M#FvX;|#twE15e z*il#1b6D!}s?e@rgu!_iTUyqWi{;qX2lQbId|ZT|`>u|MgYA25SI3S?4_B{B)P4JC zbeTFH3vFhfnu#ij!($j0!^wyQ%tXj#-!1`SF%^rs=QvL@6@>4 z`#tiFjNVngd}KaUH~PHzqvpu?wtL8k?tJ@fAuRw+WK&UAQ~rP&yEUGh?&I#Eova+Pei14}#LLHVut?l!*4vC1_x}+4{p+qT=>MU)Az8QP?0Mt| zxpvV1=LQFdT>byxzz`k^WJmV(|M%m%NBVz_P7zxGm+*#`S@rbEY&QF5p8mdXDzvZm z-&gzZW(w`3_SdtWb^Zgc&OgUG|Do(ycCc%#27m6LMbUtY1a{X!`~(u!aCmHJaBOJk zZXiWjISLHj&r=BQOK64en;=np9r}p+4vtx*H!N3SlI1g5C1vnV1%xO*(NNUMzO53o zw_?PyF4=`s4rj0K7{I}|(ehT>d$_|21FhN5=0&s5dU`mb<`mp6uBUoy_Pz82R{BWQ zeoj>TtfxnY!JU-)Hf#osa0~EkX>8UZ!8bQLDb-EOzgq69<=+!Ef6^B8Q%P~{{LWSJ zdkA!P^5XgB0!LD0Svg@8J^oF)y$W>cswJ818$BsM?US&LJFDVde=n`S)>{AWTLWH( z8_L~D@HPqJu9)qwSh@HHD`xf!c3rRO$w0l79DC>0Yc1GeK^^~utK(-t(OU;$n7+2& z)xLorCM%%4a|=V;1yBYiciaQGi};%Au3~A5ax%I?_NIUlU+NJX0zSmyw)dqNZZ0AT zjbSKBFg|AhC0Q4vyEICHFUtDpHW=l)e~>8a4rg#*n4d1p&P^3AF3l9C<}c1I2X`C% z0Qs=XU4WdP=>hVsxd9*@cn5m`=Pnn{6wWWC^>NyX5DtbOxmyADv=2QBq@n@!-SZIB zlUz2feYVYDL6xXe#o2}B+4;HTVWL6(XJ{!j-PodX-BmkU(ZG8P5xWwy>CpXzQC6*E zQJm1{`XX=w>#zA>9j)GGtvDyM*H!=IrxDJ8#!l$zA53YK_!b#Wxuu45_s4vIQ$Yv% z2={s)73^LuwiCKP)$uFTIk(ZNi$WczA``V|^3K#fNHKOk*q^LfkudxXT>Y%ZVO*ZmF!Zl~#a-d7LrtB3d1!@HP2#NyS#;}swio^w?T`qprOg~HP_ zi*qyQ3kBxzb)t5&T4&EaYB0hInR_7*?QL!$GZ6fi7`kSKhNkWFcU)SSojX52^|Yus zxis@Ape8inGnLbEtzrFrc_pX3Rj?2MrNR8+T6KTB%~ahJpa@@n|%*T z3_q>6S$7B_w{hFvSm&~Wx-taq;EcIpzy51+_R`E^)SIdEvomwcVq>09*r7hw+iTh0DzvT~+FlVRjg zefW`9e&ka0xpEa{0eH&8jU~uZCTnNANnMnzW}J){SOV;Sneik=ShQ_qYt^oSB(FF` znX4O(Rz}$pJFM6{4z-_nU5p*hVkIcs4zJ9zMoF+@vxcp17_mRpS}v)_NT461hd^@n z9sx4|rblR^stXzw`(dmk`wEO~MscyxYB}|e$36iIZB2veb=wz&_wwNV!M-5Ud!93h z&v1>_JWQfmajiXW#Zd(bxlmZ1(W-pJDRp$*G3sm%St}t(3fKU_;=EZmGXii@gQzoP z065loUfkeA8v~p-hrIdl^L*Uri0wWf!1LkobG;Lg3cgyLjUWFBpk9LbF~xByiY5wpdO~_R*{9zawR0q~WlKZ2#yhoIRM%=+Q{`|FwC+${ zR>0=g9MHpKYK;=N-Bw8l6zf7!gM)6!0V8l_?J1PEg|ul=h%nC%d27BX02-)U{@;1beFTm#cBdi;10l6|_5dJG^2Nhj8A>YdnI0<70nYJet(MTLN> zHG@x@{Q()$)TGKJtnQRLf}ez~8XBL(!R)oLK@jI5q4S#NPBfS2))Y<$Yi0V%hkTf+ zM_GfYRwGo~XwqQI*lY6TPKZp9>CnyFtA!(ydvPP3o3Q2mYHogcW=xHM0kPd1J*Qbr zqjrbZR>MQt$qMJ^FO$HeMuhj^ZbGM1k#@3NmN^^`D9RA{?wkR=SRHfS-}#FUhBWqc$fjAb?}3h5TGFou11xdf?Ld(2EJFZQ!b zTP(I1WD2Sh=C$-rUAs}+5HWDGQY}@m007vE%McTqNSI>|H%&;)1-k_S<#?9Dm~Dlm z2&THyY?50bwg4WLjtRhYT+hAr281?RB#hJ-C3ARD$9<_KTY&3 z(@24}#H>t~JQ+!^Y=KgXB>tR`V!Gt)nul7t(koA(O!EO8Z%x&(wpP3GKrC>E<;^w; zoXV0o<648$;l)+-H^tfs5&U>EvvMdTkY>E9c#>drO`^KOlL2>;$E~Ezs}So1T*4_c zAd+mEwC)J^Z*hx+J_W5k>Qa)j@x;1=g3>OW$vo7&B2ctD8>_2!Q8J1J!vlBayL_p6 z7rAG8i%Qt_cB8)Qt$9znkwuhP6Hj8&tD-1f6E?f1iI+8rX-S2g-P&e4A+M{Q@VwOZ z2+vB$(Lfhbb?eD))ytx{k<*m&PfRdx4ok(;L`ZL_$|f;587Ur*(#m-3Yuajhk-!)i zEB~4qNm3X+#?2bwKK2@}g%uIkY@*KF-X8bw(s$D^t9slSlBnv`%56^?a+ye=GA5Om zNv5Nn3mHtrbR#_RE=bACg(XSz?U)xNP#GDkAX5jk#u!6_yNIe4^H*@ivXhSKpJ{6rlGnntKP+2zyq^oyn>VKt}UKd@`A$ilRLX&2o_R6)ISL=_@v znT%j^ql@U7K2(6Mzv;5Fh054Di>EgvR<%~`YO4HvIL^!|6G3ddX-Lon)l~~R@krEQ+ov9S3QkCi5PDpP*QiwaMsF`n)_2L(7-m@y zYh-OSqZ7$n?!e{f*#olT?NA27RcbwsM`Em+OYx#VLMvDhAzG%lz^TPmVJ-QsEN4a} zH>2jx3jnEXLZFWu#Rm0aaU0!WSXI5w)v)Y~5{&g8ZQ0xAyll$YZbH*Z>p+DTPwaM8 zYi9pO3lROMS!6Nu#no_e>q7ea#hK;B*_kB}>=7L*RPShEO@QXY7;A|ft`$L6c~t8r z$$Hq%haLvIGK6yV#wI;tp($9TJ}^s)%rj5nxJwYWLx45R(g6`?O7o4BGG$Wun#MYE-07Y$uNcAT>oqA?WE!hcBidGZ=q0&}s<1$=2Q~=suWKpu% zwlxkX`r`T|O$x@~L~AW$m5r9u_;;=KSQd zQimbN8J^(?y@r;qS4%At@k`J#b~d3Tc8Gr2I0I`4UYNIWkQT90PSfpzkSodstF&R> zs;Sm!o;W34qAA1%+SZOy#99RQ(-HO+kVY)*&~xR9Hd`&ZSYVA*ocacs?Hc_WMi*J1 zXooiZ7iX4cWROyup$j46mH|=L8rCzHJ_YY=LtPK>6=k@ECL?7TiAvw^sD@e81Q5SfXk(TLNZ;Q z)(U6d=)EiRlSJ6xhwU9^rwbRB76t~h9R3<73q~bbzBo6tsI?>c=p>hBr)TDuXP#Mx z(PDIxr_K-MMwc!yltEwnCVe~EciBEbJB#(2j|GFh6_Lt~YP;g1bKAMjMktmr>z71~ zs*7c5PaTF@rI{9Z`|EYbfkPGRcS}WM*HU^ZfQ5S&KYW8;)70^SG4@W(-xsICYtv7mDp`TB0*u6H(mKx!JSJ?nbm20k$jE z)sAsGtkOcfW#sG7Y&dtBjc=ChW_q?Bg!8Ko@6Xf1?y4`_*jKWMhQ|W%YOl8=LbIQW zGEJn57sMupk{7x?y5*{+$CObcX-0DVM&^f)mc)RgpnD5(r17N9>1c8 zLb<`SO>MPv0(%C^Ao|=G7JPw#Ev-l(|CGpw!TiMWfN+$&^8zxV%u6$i;>LIa=f<|+ z63eGDTUMsQEHmdXAq%qu^jl5QyoqYprJ3{R8QpnV^<09U9>ws7)1O%$9UMjaA*AO! zb-h}4h%IPP5$%X9m*==Dyoz0}*6WSy>JsJH2qG{izHr9X{DUg%oqsh7kUh1*S=gR%q~s3W48t?Il?*|7k@?@`Z5VBp@V}tXU@+&?T*|` z&AHamC(TD@P1uY&973jNE{(|sk>*D|ipQ$U?sPb`mRy+piTTC&=t-WV(*kY1_IzXqM5dXW*U6UfR`UqlV74iRYm+Y<`;RA;c2-QmQ_#!!cqqSknh1nQ zfAdi0H`^lr{$ASuLqw!|RK^beAGtw#>BavUg#X9z(BQuR$NjkOfiH!NPS_XCi))7{ zqSDcH`To0h?8p4y_wLyD?%3TFG7Qn;)Y9y<4*KIxaSr)2?8er~kE-nO*x=CEz^EJY z2j52yznPK#yGN|)9?+i(6m>|MNZ#)BgvZ=H9^frDMD`YU5%wnuBxpNp5YQ}tl5pf2 zwO2uELL>PGBh~Z@IP;-E+F29TMl|NoZ&%sCXKh2o;9z`w-PG$v64+L1chy)UuC_6a z+2qaJBe&Kc)`jDJ%Mq`B*X9l~u$bps`VOD!XXX;S&Def75q58V1W)!>b%v|s>|sui z-A&c-u9ESW3ByCajzcsPyYCVt0LI@|n+~UCD-~gVwC=h;m)1&#s*z>mS@VcDPJ<8q zx^Ipx;J&dp`H$&EU2m!LP24u{+T+MEyO@Z2N1gtl+iEcFHhV(Y{(yNSeWjI`uCcn> zcJy`?$-rw>S{?PIHJeKeM!9=Sb(}xmC*Q7Pi$E9P1hUYH0&M2G7h9!&W@%w|VP-t; z#^ykQn18z|j=#jk-DeWO@wEv-F3ir6NXR++Pge^q!VxzVt)!v8bCI!2#Fa4$4ytzt z$G5W){B!lg-9oG+;;2^tX>BGVXNPb(Ci3@qbXbw__NkRe&VPrkvKLu{jbU}<7cp@F zWwWEjpeK2?I$HLQD-T5v*ac#lq0uo{c7+NuJQ|r;T)k@u25s(Wn8U*`fyq1`?j|Pn zk^L0jpZ1MOWH6Uqbf?w-x^cT0hCx<9anD#WTEuUQ*tE4`6?P1;)@oNj=oDnYnBR(| z0g1S0ZwPbGjaBM>$;k5oRDx%6U$fb0r>vEHcVsA3`i>)=j;fO1T1etav(CaMQNmRQ z3hGXw{N->lZ13X%w-q8kwTqGQtc$F&2{#1?R}0w4Zf*CUE*JQXJM6}aXxunab=I3f ztMJ}<-rcN;vf$u?D1(ad)bP>8(-SULOhrba;{G<2@hGav#$*1u{Uef7S+YyO`j|veN-B(OpSp0#`^JrN%Uo+l;7XpYW z7qx+!9rSzEmL(|I6dJz<5gm20h&rRwy`m?uM2sRQyQCt@buiyO5wVNh6$;sbdIb3! z_L|-`?dU=??jOZ?GiZa4IC&&1jO^%3gX{QhS?{q4b;#$d%>ei~jn>*JK1Rbn zFoSNSW_YcmlyMyhGYSMVm7Ue%My(^c ztU-icE_t+^A}Um?IcqQr5lb)K$uyib&;@a1({xCCo5pcqqgRR;MOkBXd2x1mrm!^o z7iDZQ1`M5MBwnAnc=qhfBEQ!N5f2`DC5$Q-NUBmptyI@klIoK>9IS{(Hu7g?pOU(9 zvk8;?PcsonQ(T z&N<+IgV!UQR<%xSj(MrIS}f2S%;8jDfSCF@EZ~stU&?RBDDHBIIEgDfLmBXxtqOnQ zX!{B5IU1hPoLSX>8tUV8HLNES-E}wAiiq^v3h~@}yPYERwttAD2Uz4G9WR|YjKgtWZUT^ zK_A=ctf7p!OQmcCH9|(f>q>7LA*n5SMBD3Cf`#S~zV0IKujRmH741!oEomfT@Ft1yVI3_t~<$SgmXKh0T7?8_p9C8mn9bzf*vYeTRcw zt4%NFKU;qDX&vx*sOxZOY`e7z_+E4;r--NmZX`7wWYR>zAtLOCAqoWk-|UA&g%wgw zwKnQ?9Eju##%G89caNb}!2lmHnyy|K>oQdbRpf92@jx6L9XUX|PfHcIvFp5E+=h=} zv7?QY{efAcocL=BIdmu-&L!C=#|flVm(x(jeF;moUI}qS3D?3918)OhOh5|TZ~3~T zeJbyHEQ*92%mh0IY9e+pyh1nFWlFHE|>vsqAhc9}E&nPW5_#l7m5qZzgeu>`E=N8>B(8IUs_Yct5 zFVg#8q<8qdNcZ@>O_y<^R$Vz!EBCW0Z>HVIG>gD$3IV>AEwWb(&xwMlMt}6 z_tNJF>GK2h`E&I75#5%@bp3ybKJV$$j|Z&ZZ|{=k;V#eP^!a}JyhFD^(dWlw($4Rr z&%1QH9@XjJ836<7e(|FRxroC$?cb&Qq`pTRK27(JYk24j^j(b8{?Y9|l>4_1K75eN ze>8@<_uG`dq|=YkCrR&@IsK!tDf<4RRo+j~=N8=`(djSJ=Z1!d^v}`vhpl_Secj6c zd`!k$-oGOTI`m=v{$13gA4LBVPM+29KW3#@l>X=F`y&MZ0Sf<@(dLhN@Lxmy={~LV z|6Te#`SF7f5Ac0F_V?-g{V$pF60v^__>_KBr~ha4S+M%+-_iHEj|uMMJ@EY*x<9S& z|8M$Ktn&YWK3mrP|7-O(>ib9Zz5J2~|8@P|+}{q|e;*;-i`IRNaJ~dwAByn~M2xj7 zt1)8w*h)KAn0|I{^1|#?jBVbD?A08n87s`r7uuaNajAt^QztCIw|2GG*o-xrL>t%e zMXzG3y!k-yo%;1^t5J{fX;5^JqUE>yrm))FFk475?4HDkrzbclJyyUOFfkGcwve5q z%rlec3-f2sF3l_#mM71gpDD!T6%H*UKqb_~smIt%F;?rnz2IWD__1%*HP54f_Go}0^g&i6_Bm3e1`8);|iwvSN;*- zqvjQb`%myaT2KKw_{yt%kCs$0&A;+tzDJ8HK=-dS`5rB+Jc;}F^F3NvIfwfNzDG+d z3%Gxh@6qDQGq}(4Jz8FQKkgOZqX#Nz&{rPed-O!5iTl`XMsL2Jed)iv^yQm>@cK*t z_Q3Dazc1YU%)#7$y?F8BOMkeq^cOw~D*x+9_PZZ`dHTr1p8^GV=RbW2gSv3*B?5Ev za|dtb=(~cseCx7)@;ASI`}VEBf*E{knI2tvrE@Iy%C8;+piduQXkYykeZTbU=LzP` z3H*(9zW>#iAH4mAo1Z;+b3C^3KdyZCmEq66>c4K@IP&oAjpMH=uEHu6wD9RqP}%gH zh}c7){w4aoQ2Ad`o0H#0Z65jTE5C+E#lnS!2~GM$1=H%%tt*sbq4F3KzaNQXmACQpDew6?6#O5kNNUGYzV*lSOLG4RLw|Vc<~M)(Lm&3TPe_XYM{<4j-BdvrenQ}1y7esQ zKd5_6kN48AKgsxkzqeid_}0zw+a7+ztn1dV5EPo<>^3DFHhWFwGkNAHyf**dtZ4B?>P>ioQ;Aq+_M67;Cbzb znNb(vsxy{|^}A>5=`T5dClS+9fY!5f%nK^EoQNf0zS(G9bIOO5=msi;UAOhzHQ$D( z!1cFj{r(5^`4{y0Kk4(I==0y`^KJV40Ih|8o<13@ne>^X&ri~)PM;5bOwb=6@I>GP zz#m~wW4*jZJR|tH-@bY9Vem_z{+WXhGY@w__#hC)AJlaK|AuuNe8E;s_&4xI;QwL- z7r(#wk%JFo%?5D$>cM&%s|A`T(^{& z_V#+GxDrDEQ2DNyU%W}ujAbfCT9Py6?K&lu?@lY0X*snFEr{}?S?eG@EvWL7*Ub#C z!I@Trm(Ps+Ep-|#%2bxGwMK^_BA$Fqz#*;~C2Oq1c`LTt>qm9JU<_C$(EV&K!#jgF zeMqOn7=!Qsf*wO$2lXcvlW~4Pk3DGkBM*4;#X*e&Ih_~jfGfZKpx{Wb+>SQbn8;T_c9O)DMf z;*(v{ZCdHhQMz+o(tXHEhq=2z>COex;d4{B73o+OOo$wK&`JmRKdZly4(qfcp-{LPqO-g}PHJsFYi(U^z3xPJNjL{1K-do!<){^uS^d}ym)-pJyisoT>79cY~1OHTskrV$*V8wZ?l z1&ghp*Amr|ZR`xE6S#P}QiDViFw zd5*jFlBbd8=1){4x7?hwZ{8<2VFPm+r8|=}*yIcyn!TzDy9%f-5{S0*3E1?@G2yEd z>>4fU)ANPJ>C218zH{|dR`5yiWP91#=c{9v^Zt0(riMx zVocxM=m7DOPh_45ovdzp^|`twrlIo%C~W6N<(H(@m>-EcFDcqT-8XIt&()u>tgTq_ zmRx0{s5g7T+Q0VnviLuov<1`oJuhPi{x3I}8w&A%L;L5y-UUJ{h&d}Ga*2@e;EEOQ(xK!^kZ7?f~U%sN#|XGlCw{}Krfa5{fs zCO)Au8RAIxjBlfhfzK~2yL6e>)@g+AJ^O6o+|1fu^2}mF$AWh= zA%_(qzJFSN(hv6Gpl>k+RtarPzGRhk860l^Wkk~|b^4pFMyJ8ycN0_d7ZzqIla;nv zZ91xdwXt69^sgcce!n?Tw10hYbXfJz=hbrw1bphBJ(4uNmZG~%VtVGx#iu;G3N?{P zEGgr|fBCuxW%RcH$%8x4<3F4q9>Usz(BJ#(|NXeS+5arODE2=K z+Tg|5|2!7^!9BPC`JG2&F??+MAAa^4y`v9w_MZ#b|17_YKA$}JFg`ol|Kww_qxAd< zU2MEdUp!#d3j0miLsHgz=yQKa|Aw85(G|m9=+EmmJ`(fv&p)oe(S@+L!3HC2cfLqcKx}C`eM%tYu4lX-k22_efE2;v=2q(x33=y+&|VOovpK0 zx?UIq+`|q9b@}>h-aYnK-v9FlAI@=ozc2Pa`u@zv#IoUCRMQ#y{@IToeE99UJ(ub8 zwU2vtG(Sn7cMvXqS>Iz4ev96tzUUw9LVcOaU*PiI75gxK-}t!LJs~~t;!D;&HfPV% z{Xf(B{|0^1FN$3e?*A5je&^$+ezDjm@t*GA^N`&CD(drb&&~dHK>to*gAEEnG^nOaiR|h=!D8KK3zr6eb zp%obKM+h5F9+2@i!8L0d_6(S!Er!IX zC}fgU;G?dLv5___V&!3hPavLoHCB0~at?U=IZxd+3J+w;p;-#`Ga2xcd+hvTxBGemjU7rGGmSzJvHC zg7`4LiO9VJ-$dXZ!8Z}h_u!i*XTw`_2ehT{%RWEL`cu!n+WdtPl}z@#h3nY3H!vcD?jtuCkXz{ zUw=RDIuG4Cy#38TdFdOso~Fz%U;WlgU%UF*+e823HP~3a{+h9|LMv?>E40?Ou|lhD z8!NQlwy{Dl*fv(^3fsmCy<*!~p?7Q>EA*0WV};(bZLH91wv82f&$h8bFWNR%=uO+k z3cc#tSlvE~-mPHE`YT`mHtx~Ol@D|K`5wJpd6C=C_vrP?tK5FRNAFjDk=xJr7=_9w zxc%Q2`;Xjz{pjy?-oAJX?SJN0AN~EwSEhgU;HQ9JFa776fAIRk!s4Y{XTJ&)Eg}=Q zDxB_b9E5Gw3i|y&UVZgdjOnj`?KPO$;BTz+1228Uw!@;~{5PL{E-1k z?|J919enpf1Y_pq&XM;V`1VWRc;~OZZQ<5G6$l^w zOot%6?W^zk>i7Hl`_ov67Ao%mklzqTp+j~^Yp`L7?j`T0kFhEe35 zKQ{^+yq_#w#TefFS`$rr`R^b32(afHuiQBD-U^7&^QFtTeuZVqDrcNkE{-c8`0>>PQllcig0kzjF4GlfU)C98AWhk33?P_0sDPP9ISV zw=Q}mK3e$+dhr!BSc83e`p9E19z0G1^2h-ilqo>_?2UhN^S2)PnQu$)eEZul*E6-b z`L%-=#X#_}mupAf`!7J8a{u9_Z@l=$7pITB`}qS@{0}c&zV+HSxe7O5zrE3a>j+ig zWtvZ?=;4A!zN>DVKlV+=s&`T%YQv>h4j!lczhLM8gKtXyZ~d8bEBXK3H#z@- zKmM6-O1+=gqe{b#fz|zY^K0+n`qz#msS0VY{?8qGB=>tK|N9G%($F3JbAS2Z=!?_u zdGyT5|9<0-UO9VUfrs(pt$+JxZncJQUC`BXtZMxww_5-3&$wEnsFoNE{!9F={0cY4 zE0m)0iH{wK-I@}xUft_%-QN=!|DX99SYB$tuO0#G^zttsfi&{@hp=vf7q0vw!C$&` ztLfJCogevawEHvv%gxt+=$#*jUHR=JA8o#J@L{%B{5t)3O`P(2fFF&|&>(ktp zgS@6Z|A6%Lt=oU9jYMyL`}W4kTc7z;8H_K!?h3{y+^#eHsSAWZ*3AA(BRr+^CurCzUS7DqvM1hVa>Z}U34UUt$ELmoX8JA zZ9b46%jGSbP|xsFdnc=+yq#HCoWFpOTD3$>apXb8uD!gQe#PB~zN$`hbR;B_P+XCT z`Zv`noh}hOi@oaAMr*y;kpe5umMU+oH#16I)+ag~?%zDDWRp4(V{d8fe|y9&fBN8499VUev% z@Cc_G`VRAygxRg5+58qIKAIieQi_CF+(Q`^J6eiu)Sa!SQ^I){GxKLD(bmy=j3LT~ zzC^r7e6@r;Uaz*PW6*3lGt){Y?ULfDhtbOkX78o>=7}{B?z6sFrL|ccE03|{;XXF~ zM2#1;QT4_q&f<)z#x>Di-yqKBA87dtj9Jh=*U0L)9w0gIUGzCjpFDle&}Wf8EA(m6 z=P%Lcuh8d{^!dm1`B(J$J^K7-`uunLJV0{ayXbS6K6(0_(Q@E{nCL;E(}1kcw!1&? zx7~g9BVxO&{*u`4KGVf^7rF%KW#F%4+wQ`*2fyKS1UVHDM2cbk}0uxPjK(pmYnCEwZUX zJLcK&vL5$Qzwe~ck#1Y*u%LeV(P*3C4_oPOQMy}Q(tX@Y2ioy>h~!{=vXp6xK} zk>3?qe2zF4w&KRLs*G|J7V0TF3isu=a*Es+DOE z+ui#gkovjz;#vE3_g=8xteFpL58N-g_wVxc(}S9VmEHUI1m6E(;QogK_do0#hl8;{ zx{Vh6wHM8rorwKt#QW^ed+(1~_YcrL?AQO`HM1`NKn!}{XRP-7lfBx;5f4|}2 z0jckoW8!C=IN;yE_@RJ){>KjZ_XCa`@b3p459s3u1N!>Y2mJdj(}DefpE%&(FIha` z-@mv@_y6dngAf0%uJ39<-@ksqzkl=7f&GV%U_anziGS4jf9`;PzvQn6_5)r!;NRc) zy9fOHG5?V673ALs1m3S6@bB;Z#sP1>D;m$wAMo!#{q6z({?hLwzcv0}r28*g_x~YK zzyEx|zrXc*VE^KO(*1}3GVvb=q<-)zw)d+Zd%(Y+^<#AZiI)#P{A;>@jy>StFY155 zzdx0Kz-!+lRKNE<;NL%*v1q!&AMkl#?J}jH#|om!q>EX>SJ7YYogKw{tec8r9{XfOlaT9+s)y0W{b$$ozeS0dUFZpS?=Bwy4OIqUFM7B7QuA4dZM?( zu<66Xs8lex=f!|fJASPj~@*UHtF zvby@jO0}MdmzpL~=v~lAxR`;W`$!TUmU45I5Cb$L(Mdf!V!BuQnSO`Lpifh}pQ%#B zjiGhi1@2?je?gtrd0-ar9vf={pq92-G2ccc(oV(MR=C(A*Nf_X8a7Up=po{qKGE|ITGcas#gY?@$);pY!=#et-SHAJ;wb|JGYm zbkCbz`WC^!aN;w2sPe!|>Ns%$#q}m1d9R*wh+aKiZ?szRV7fi6WmhlmJK2prJgx&* z3pZqCJ%Xn_*&&9+>=304@k6RF03h!H$PTOA$QWRZx&T_vCb1(9{Oz0J%x>ueCvrn7 zJ32NrG)B`;wHl31_u^0V_A#eNB06S8zA`_|F~g-+a#OEB{cBu3PcW*-a|Go1Pp^9!frcbdWeo6}U_R>agtu?<+aK zMFmr9t~`I$O7lXfj>-q4d+nl&DN*A@jh579srM=QrN)$a&<8Vlqw<0|3dhx;?Jyh| zk90lVY_#QMNdd!2arD3yVu7yS#dz6gYOyXe325n)C)2EfOa+G~h-ZFt7O#EgvX1Hr z4+IL&Aqs_PEv4wgu!uRFK2}d3!+EwlRUp!IhQkt^K0SD+m8qjL)m;^7%yRCq$#*md zf_-xwqi^+(KF~OjGpfoV)1h#+yHwp@eOwyHCsaK!0UK@LmxY~PUilk53MygL!yF-H z)jrC!3rVU^jE)O}`%_{_c7^?QySMa_q1AC867=UV8Ilr|hAMRo$Jh{n`JO+XLr!3E_4xBwdyhR1@~t1=#n^{!?B4L;2aD*l zXRH@$C5vb)I9nr2e0bX_fw4A2wqpn)dx5bUmoBA=-zaT3;}N6AT}K0Bk6TncB0Tp( z65+0QR3foQ^u;AI74k(Vk{CiQjv*wq z?UY?TQ4HiSGNF+@L>)>g%?EF0Q@f~5#e(JrO|o-|)Zs-zGSAKAl|lCKdzBj_fsRa+ zBOi)~*#dxaLJ(hA1pHY$7fk(n(0-{Q>V^o5x{)-U1OVgFC%M}`CYf4PB? z{r$iDa(SALc6Gg3!$!k;gCwHHTC2E@)6F^}9<>$p1CZs~xO0#aeAUoxquC#3$>STU3+^vQ>vq#G)AlxTQ+9nMptsq57{BC$G8> zUHN*wxUE(krO#?(CxQLz3SQRyFW##%cG~|R$eZ{t!vlF}CUe=5{rDgE=K_ODW3WMb$P*w_;IM)a)TGVh(3OfvpZsaAR9IHzN zXQ8&SmMPZD^rJ|?h5&5jWK_+D2pH<*@c&hum1Q?`Tk3VWTCX>*OEZUeIRFELn1pI* zR0d0Bm4@yIE%$BH=CY*w_VoyzjCk2xRdpNfcO8i!z26W|J|2s zfBoNI|M%Db{q=u;{eNp-SpN~SABL>=!1Rx=-s^vUBs&88zv1k_@IZEO0Jcs!BG>!t z|NXe`f&Cv0v`VU972We4TGi`Li?;yzgorAi5TV#0ezjO~?xCR{!Bc9g(cErT*D4*A zoJzqKZm9p{s9LJ7H|pxCjJniVtI382D?^R$vx>8697nYqtDVha%NbWji`jC@RamU8 zY;>qK9YrMe1htK(QgwA(c1AjCqh5BTz0}I}wy}$PYVM+X%Bka&8nv*oQmdBKdFrEj z+fhX-8&BF5Hil$C$Z{67SkkpnXBz~WeYnPzQ^m15iaM7~{qm*|3Nfu3EryaTc2N12 z-v3MS;W@Qp$IF|E?f|cyWpm0-rO|XmFHDWxtk!B!$!@fr)s0%3VNhyyd3O2S{KaK8 zIrpr(Jh`|yIk)`mIHeXVGv~Ua&k9*@)~W=W8rCY-JKI!$2616#aq1l9nLIOles=j; zYKA&HyF53uw4~0?FRDqkFuAxqJ9Y8=&hR~y%S9z#f&~$f- z)mmFO{aG4ZeacV;MqMqZ?|GF%2OH7U+o(BFM;={s zT6L!u^``47N$bU0tx<}$I#q64lz6!+>7DjnHIUl^i|H*YrCo(7VX1_j4vin6l02ej z43-vV=g!YhJ-7ceO@b6TOVi>VkeKW|IcYWOM5?8 zYsIy8$_M4*ToBGi-9TzN?GDYM9e`o$wp+!@jm^59y+_#_OJE_aD6slfpZ=jiPERh_An9EI zq^#2ID3E#q@6$;b;Pm3`rI|$wq`V9QMK7xwQT!TUveA+0e3*LNC20Vu6T|m4s;vX# zakaWpFNt*v-rNe%gqe?g3n-KBxDY=>i(DBqQ_s=0R%4^d>w3GoUab)mS=lt>(&m-7 z&umqeJYEO=dyUsAwJ>#dbZB%?IT+zg!tS49x$L1wvOCVeLP$<2qppkx&9iJ&q9-u7 zlH;X}NvVZ))vJ7Xm^md3sqE}XU6o^~csuwSJKzP?^wRO9D09gOb-28=^3w%OVBJQiRk~WOXW5K=S$I%xc3A(0- zxkg^k9c{bS6La&lRjzgFRdzfMBP|PQYxi5g7?_PSWM4c zlGDP1Ljkn%3Ppea4nyJ9$SE7^rEY`4a~|ViPf&NVXrf0fSBaKyny7rPBp5Nxw_2pQ zxb3vk$h}!1MzpBv&ZaeYUF2wIh||P7Eyh$mya5?laR8!top?yB6wFtah@Gu>+7Xjk zU?;oL>0RKACg9N%*^v5f7^5@a{WR9~kC?<7w1+snQumz1iMUl^CDk97hxQzXELkTh zgUvSC*IGp|K5>sVAW%s#D$J#HQb@uqhh`^crpGl;;?Hy8;_}Qh<1SYr<-$|OT@#7O z0})*6j`JL?WBvT=;kY$GX2q;tJv70Cb?lh(p7oz*PU+b6*}~lX^6c4XdGPG_#ZISK zs<_?Hs1iM=G&V&LH*Y+`#LY+Bc8#O(J-GucMdo{nr8ZsD73LzlQMRrKYA73{0K~&m zg+=8eF&4y}>)Gv%{_`~Y&-3U{ou8eVTjrK)Z-IE+>PN|``t-MSMgM6E@CxjlrK?m@ zo(Z<3+_qAE2y^ty{?mdY8hz9;Ch91t zFQAO+QvjTXMuJv~TET5O%l*XrS4kw&=Y8rP*ZI~rhD#rk;46Mb@0Fnr2jZfT7?1MpK7vmCiprQ33( zaVByx>N0uEVq~vHm(e+^3KAYSxu?-Phc}ue+n3viJq~b8A!uf?UT%v!qnm$j2sm2(Zb(JPz06Liyn1%67X-%buoMtUk~q3sA73(@Ta0uy#?8~7{H5-@F#YwFo6_qVke5%# zMz?anih4;*DK#97;6}?OnF*O8Od@=f-&;E{?E`C)ON|4>xMMF#DZ6tb)*C~yQS+dS zkh^y59+%S3r?1^1K50;Uh*9B%Lm&sa;oabl%MR19o?~}V8CfC*4lf|mhiPaY3?1dk z3H_?H40_#mr&_CFn3aqHVUL<6*|L!~D-liSbzwXeGo1AE8uYL1ZmR*UQb|m3G8$u8 z?cqbzP@|O`)Xp-r#lyl39c^U;{`JOnyf^hPHwanLOFGJlUJGOi(TfM$?Q??+y>B#~ zmJp|P(Z)l4d?`nmZ5N}@HNy03eoVg>vHJSB7-jJx z*W7j2uqd+XQj_70teo{`XPf8>A9n`zWzq%WXQ`Wi{7rj9Sctl-N(sytT1oSFY<2FiGp7ccWUHUeZs-{ptN$1LYwm zWe}xC&&Aimm>wdbhap{PrU&LuS7CoYh1XyYvbzTR(5AbJCtP*6Gsi=(y;a#&qqwhJ z?(mSA+6klC2&Ax(FoaTi+<9s6u!@;5J=A!WY&M8S94)6zYwgCSz474NY7hMLPmb?4 z{4@0F9$;X2)wV|_v}pH2Hc!$E;fy6ybj2DkjfFQS`rIOW(>Y623E;RTvV<@k^sYB< z+BA=mFhr$8@{7Sa=vk78Kfzc<>mQ<@wJcpEFMiq)(0iKBrrXsII*nE985d+y>Xp_? zr=QrVG^O_v=?9#~(B<(& zSOn8zp4r1gHC8s-ZHGm$yDi2N-8!vey}jB+Ps5Wgt*lq;?KMT8_LZw?K&Wr5ySzB{ zL2wCOFY>(FS_LmmvJCHJA1yP4fHo-#P%IVrclM-nT?R$wjRluer2&v3j7xrd!_Sg5 zxQZ5@YB+6TI=fTGG_h#yj_2~OxaAU#!PoY#cLOgU7!Fx9cZd8sNT)M9;4c&bKQm&s?}8(fV8$d%v^3_+(=qVRx?gU<oO+-1rW)&pR4u1;y$4ct$EP=n zMIJuhJA9IqHzst##py^q+X`l(o5t9Odmpp9VU~+p5EfgFS`V~J#NGG`j8*G))D8j4 z3^nt%CL)H~CR@yfbw|1=wlAy(5SRZniW^het)p9M6hkEc@!A%~Q0jq_(cdP(1D-13 z&p7k}#$j=0d2x1TY0T5CQyn2MV>l6x!Q%M5T+6c;X67$0Te&*bbqB^_5xLx;3UN}%rhggWM}F+6)GLbKjK)o*x2z@7FonKfJK&G)PqU%;J-0jvqe-U9b;n#n@I zglVU+{}Ex1Yyvic)?<<`&ri>fS&HqV(qkgq0sPF`vX$Lh7C!rA`+;>&#w8qztBs~Y zK)Osqwh@B0kWft%p=r^7T1&KHauZ0IZVKfIhdVP@ATG!YzTVZ8s(b$$ieh&v!j)Bt zq9EAQ3kkF7d0~F~;`y0`d=(}yE}xrUOdj@GvZ_;U+&`h&oLyL+ou5k{raJbY*=Tbl8qG0VNf#y;FL(x1X?3_7c*(%56o)OFA0$o8 zJhL#rxLjCz_QIL@^U08T3ndAs@Yz~Krm%L^yF=*l-rE7`n!U$spYEdz0Dp>zBo?a{ z3Bp=%Bj4+Z6Zy_a2`E`;84jh8(G@{YVNKDf=Zb((jdrK1F8o35+O=vz>6LAn*6#p_OcyRFVQ)~clx8%3nW zq!(PzHr1oKp;ie-^6cc?$Y@&4%q~q$_M>*iPL+Bxl@a$C4*{ezXg2V^=0=Nkel*6L z326r)T5qX=+zU`tBLET{U(m%U@1#mnW%Jpk3nZ7$ieJr|(-9L6*uS+aj?>XDR7D=h zqFRM*VsRUDAhAGmia6>vP~F6ILP2#=<2EbBe&)#1?4JYIokr`L9^>_Gb#`{D|I*}8 zcHnph`7zNL0L|YbTyq>pnq=V`p-z2cmGFj|hB0ev1L+Fmb^;DGd^l{G9yY6WnQRy! z#7b>vG8v9KtqV`fVHTJf7}&FvR3z_~1H>h~HhfAyvzKc?xJEsrrWjv&4%5_YQyohV zr1gY8CIf}ih+t&O3hq0=Um)J5?P%a^;?>@e*-Xh-i`5ziQ}Dk*$c=eaYpe-GOd4lQ zoQC{_kDpvyFXH!;rBbG{(aEeKM2 zF&UYMMk;Vi?})8(0GsEUU#Rf`rCq&_|zb|AbJTQ{xG9G!LtwR-m$4DUkHgF4b5ArTV}Oi<+JF#iC>< zLbj6Rd#Fw8D@lz{sT&$6)R^z_u@C9zX@hy9`07%4x%dVOpPq~^e0nmva6s2(uLT<} z1sZ*E76vLw0~*KC9W!$vrFy~iwP%+wrzg9^q}jBCozjK*X#zG?xJX1|YX0Kfa?k`L z43w=|b^&&JrU%$P^gL%H3|$>m(d@eE>Nc9#@uE5%_LGKmB7W|2;Y{Je8T!RR#S?KK zYWeR=HSc~c@O~}up8ChT;qIc_TsE!blTCwmY!<65KI!c>YQziU!D$D}nTQea8wIg( z7o@F%5YMD0>uEzSQ|gq;>g~a>sg;T2jd!dR>INpQ2`!s!$)3>?iM}i~Vy&iDG&h*f z6XgY)MOTQQ6#HDBjRt=SZc6Wqfe74CzL|%&D8xV0d5lyp_=e`8_2z|W^tA~GN9J~W zujR1F*|qO%C>R_f#^nOxj|V}c9xeO@#Jlfn7liqs+91RAVvWt8_;eml)x_#n?<7+x zcR)B&IC|A&CX_`|E=`<-lJtIIYPN7`W^Q_Zu`oMbAf`VGO%BbpOS4lm`cXlUyfBa! z8@X{)v;m#64iwRz3j_q}r^3?3sVU+g&t5!F)CR_WR0R$SUux7>t80bwW}{V38bIkN z4kN9OQHzNrDG%GSmf=v6-eQY1yS2J{>eOf|wevRG)7uIbLRfxRr$T~!*9tTdm{>m2 zQ=j*}y6?JpGmrY;9qH7#9zOf^gvz=d>CGmjvL|V3Vj?RO4}?;v6GH5_lgB*rlnPG6 zunU8+5V*(+J#D%MfzHzVL>bWa+%R(Mggv#0I@$AuhiQL*>Hb~N|JbVXdr`(t`k!G0 zE%e9#8p`d*|GF=i(h4BfcB`|+dQkl%>U6()2hmX)ln54a{n|TTRIf&W-`DZ%>v+D$ zbv%0(h^o!beo-$XPDPbB;O1LVFKCG^Zr^3Jt8nqfNTn2Zy$Y-CJ5@D-wivC5t!p7p zSnt!tvs?MHtO=BU1euu!@t?A3ha{VMiY?!=Te($F9kn<=H8+XgS8FPLY5MoH*Vnjd-=%)a7{Ou`VeS2OxVKfcYmw_!VH zPHb$QO=^8loNKbXA*A2>nT=OHh+_Mgv&sk0$X-7J)|^UFjIVjiR(DGTj5 zrXc=MG6{O3-zMd_LP`L^=!V?nOlpW!@@sX~lBakm{dyV?x#U1T7rTNu5X3kCfpye9 z)CpIHuA(%36FCM}Tk|69=+t~-APan_Ppi?C*KCs@j|3tRnm1yxJ)s8tLXiqFIzwJ0 zC967oDrvws#TW+*ytHn7$`ZSBJv3lm&my8j(sevAJ_vo_SqXY$b+yn@t2lR5%#fY> zTBjnv%Ny(K+uYrfM>Qwdrr$XA`DZHib^FRd{;KEfHvw0_Qv-R|x))Vn(|&5%ao=r_ z%%my*>0x*tS5M^zV9J_TW3J+a#7rC++14kYak~yrEnKv1CE!;uMWa&PV9#A(EEP6* zwLh0TNwZpch}l&A;;gWz+CH128pR8iy>hixfIs-Q>SJfr=us2v(B6S`0+Zg2f+XUy z-?!HDF$~VR0peE%Stk`o$I4PuqEYBL1+FkxHfh$xw93+d0OGROoDO4{ZwT=K)iAmM zPckSq>UBO$I7$T&*%4v{yMq!pqD{H{s8+P~iU`AD3A(j2<#Pt~8bKWPF58jtDyAd0 z^l}3TdvS=0-FRFYyaB6?)v)w0fOz literal 0 HcmV?d00001 diff --git a/doc/design b/doc/design new file mode 100644 index 00000000..89f2cd3d --- /dev/null +++ b/doc/design @@ -0,0 +1,497 @@ +This is a design for version 2 of our smbus and lm_sensors module. This +document is Copyright (c) 1998 by Frodo Looijaard. You may freely copy and +distribute it, as long as you recognize me as the author, and mark any +changes you make as being your own, and distribute this notice with it. + +Document version 1.0, 19981101. + + +Object oriented approach +======================== + +The i2c module structs contain callback functions and data fields. In the +i2c module, these structures are only referenced by pointers. This makes +is easy to extend these structs to contain additional information or +callback functions. For those familiar with object oriented languages, +you can see the smbus structures as an object extension of the i2c +structures, and the sensors structures are an extenstion of the smbus +structures. + +To make this clearer, I will show in an example how this is done. Note +that I have simplified some things here, so this example does not +correspond to an actual struct found in one of the modules. + +In the i2c module, you can find a struct somewhat like this: + +struct i2c_adapter { + char name[32]; + int (*detach_client) (struct i2c_client *); + struct i2c_adapter *next; +} + +We have a plain data field (name), a call-back function which needs one +parameter (a pointer to a i2c_client struct), and a data field which is +a pointer to the next adapater. + +Now we want to extend this structure. We need another data field, +containing a number of flags. We will call this new structure smbus_adapter. +A few other things change too: + +struct smbus_adapter { + char name[32]; + int (*detach_client) (struct smbus_client *); + struct smbus_adapter *next; + unsigned int flags; +} + +So we copy all existing fields. The next field still points to the next +adapter - but it is now of type smbus_adapter, not i2c_adapter. And the +call-back function takes now a parameter of type pointer to smbus_client. +And there is a new data field, called flags. + +Now examine this function definition: + +int fully_detach_i2c_client (struct i2c_client * client) +{ + res = 0; + struct i2c_adapter *current_adapter = first_adapter; /* a global var. */ + while (current_adapter) { + res |= (current_adapter -> detach_client) (client); + current_adapter = current_adapter -> next; + } + return res; +} + +This function detaches a client from all adapters. Nice. But now comes the +Big Trick(tm): we can still use this function for smbus_adapters! + +int fully_detach_smbus_client (struct smbus_client * client) +{ + return fully_detach_i2c_client( (struct i2c_client *) client); +} + +All we needed here was a simple typecast. Datapointers remain datapointers, +so this can safely be done. And because we use call-back functions, the +actual function called to detach a client from a single adapter might +be the same, or different, depending on what function was put in +client->detach_client! + +It gets even better. The i2c module stores internally all registered +adapters. But it stores pointers to the i2c_adapter struct, not the +structs themselves! So there is an array: + +#define I2C_ADAPTER_MAX 32 +struct i2c_adapter *adapters[I2C_ADAPTER_MAX]; +/* this is an array of pointers to structs, not vice versa! */ + +So, an element of this array might in fact point to a smbus_adapter, instead +of an i2c_adapter! If you know this for sure, you might use a typecast and +access the additional fields. In the meantime, the i2c internal +administration remains valid. + +We have to thank Simon Vogl and Gerd Knorr for the way they implemented +their i2c module. Any other way would have made this approach impossible, +and basing anything upon their module much more difficult. + + +Module overview +=============== + +All in all, lots of modules will be stacked on each other. Too bad, but +that is the only way to cleanly implement everything. Note that in a +specific situation, only a few modules may need to be loaded. sensor.o, +for example, does not depend on smbus.o (in the sense that you can load +sensor.o without loading smbus.o). A specific bus driver, though, will +depend on many of them. + +Generally: + sensor.o depends on nothing + smbus.o depends on nothing + i2c.o depends on nothing. + + isa.o depends only on sensor.o + A non-i2c SMBus bus driver depends only on smbus.o + A i2c bus driver depends only on i2c.o + + A sensor chip driver depends at least on sensor.o, and possibly on smbus.o + A SMBus chip driver depends only on smbus.o + A I2C chip driver depends only on i2c.o + + +sensor.o +Main sensors handling +Unites SMBus adapters and ISA adapters. + + isa.o + Implements the ISA adapter driver for sensor.o. This may prove to be so + closely integrated that it can better be made part of sensor.o + + +smbus.o +Main SMBus handling +Unites I2C adapters and SMBus hosts (like the PIIX4) + + piix4.o + SMBus adapter driver for the PIIX4 SMBus host. + + ????.o + Adapter driver for another SMBus host + + +i2c.o +Main i2c handling + + ????.o + I2C adapter driver + Implementing a class of I2C busses + + +A chip driver (typically defined in its own module) can be hooked on all +these levels: + * If it is a sensor chip, it should be hooked to sensor.o + * An SMBus chip should be hooked to smbus.o + * An I2C chip should be hooked to i2c.o +It can be difficult to decide whether a specific chip should be hoooked to +smbus.o or i2c.o. A good deciding question is, 'could it be connected to +a PIIX4?' + + +Module i2c.o +============ + +This is Simon Vogl's i2c module (this one is different from the i2c module +included in kernels around 1.2.120!). + +A driver +-------- + +struct sensor_driver { + char name[32]; + int id; + unsigned int flags; + int (* attach_adapter) (struct i2c_adapter *); + int (* detach_client) (struct i2c_client *); + int (* command) (struct i2c_client *, unsigned int cmd, void *arg); + void (* inc_use) (struct i2c_client *); + void (* dec_use) (struct i2c_client *); +} + +A driver tells us how we should handle a specific type of i2c chip. An +instance of such a chip is called a 'client'. An example would be the LM75 +module: it would contain only one driver, which tells us how to handle the +LM75; but each detected LM75 would be a separate client (which would be +dynamically allocated). + + +A description of the above struct: + name: The name of this driver + id: A unique driver identifier + flags: Flags to set certain kinds of behaviour (not used right now) + attach_adapter: A call-back function which is called if a new adapter (bus) + is found. This allows us to do our detection stuff on the new adapter, + and register new clients. + detach_client: A call-back function which is called if a specific client + which uses this driver should be disallocated. If a specific sensor module + is removed, for instance, this function would be called for each registered + client. + command: A generic call-back function to communicate in a driver-dependent + way with a specific client. This should only be seldom needed. + inc_use: Can be called to add one to an internal 'use' counter. This can be + used to control when it is safe to remove this module, for example. + + +A client +-------- + +struct i2c_client { + char name[32]; + int id; + unsigned int flags; + unsigned char addr; + struct i2c_adapter *adapter; + struct i2c_driver *driver; + void *data; +} + +A client is a specific sensor chip. Its operation is controlled by a driver +(which describes a type of sensor chip), and it is connected to an adapter +(a bus). + +A description of the above struct: + name: The name of this client + id: A unique client id + flags: Flags to set certain kinds of behaviour (not very important) + addr: The client address. 10-bit addresses are a bit of a kludge right now. + adapter: A pointer to the adapter this client is on. + driver: A pointer to the driver this client uses. + data: Additional, client-dependent data + + +An algorithm +------------ + +struct i2c_algorithm { + char name[32]; + unsigned int id; + int (* master_xfer) (struct i2c_adapter *adap, struct i2c_msg msgs[], + int num); + int (* slave_send) (struct i2c_adapter *,char *, int); + int (* slave_recv) (struct i2c_adapter *,char *, int); + int (* algo_control) (struct i2c_adapter *, unsigned int, unsigned long); + int (* client_register) (struct i2c_client *); + int (* client_unregister) (struct i2c_client *); +} + +An algorithm describes how a certain class of i2c busses can be accessed. + +A description of the above struct: + name: The name of this algorithm + id: A unique algorithm id + master_xfer: Transfer a bunch of messages through a specific i2c bus. + slave_send: Send a message as if we are a slave device + slave_recv: Receive a message as if we are a slave device + client_register: Register a new client + client_unregister: Unregister a client + + +An adapter +---------- + +struct i2c_adapter { + char name[32]; + unsigned int id; + struct i2c_algorithm *algo; + void *data; +#ifdef SPINLOCK + spinlock_t lock; + unsigned long lockflags; +#else + struct semaphore lock; +#endif + unsigned int flags; + struct i2c_client *clients[I2C_CLIENT_MAX]; + int client_count; + int timeout; + int retries; +} + +An adapter is a specific i2c bus. How to access it is defined in the +algorithm associated with it. + +A description of the above struct: + name: The name of this adapter + id: A unique adapter id + algo: The algorithm through which this bus must be accessed + data: Adapter specific data + lock, lockflags: To lock out simultaneous adapter access + flags: Modifiers for adapter operation + clients: All currently connected clients + client_count: The number of currently connected clients + timeout, retries: Internal variables (unimportant here). + + +Access functions +---------------- + +All these functions are defined extern. + +int i2c_master_send(struct i2c_client *,const char *, int); +int i2c_master_recv(struct i2c_client *,char *, int); +int i2c_transfer(struct i2c_adapter *,struct i2c_msg [], int num); + +These function respectively send one message to a client, receive one message +from a client, or transmit a bunch of messages. struct i2c_msg contains +an i2c address to communicate with, and can both read from and write to this +address. + + +int i2c_slave_send(struct i2c_client *, char *, int); +int i2c_slave_recv(struct i2c_client *, char *, int); + +Communicate with another master as if the normal master is a common slave +device. + + +There are several other functions, to register things for example, that +are less important to us. + + +Module smbus.o +============== + +This module offers support for SMBus operation. An SMBus adapter can either +accept SMBus commands (like the PIIX4), or be in fact an I2C driver. In +the last case, all SMBus commands will be expressed through I2C primitives. +This means that any I2C adapter driver will automatically be a SMBus +driver. + +At this point, it should be noted that there can only be one System +Management Bus in a given system (is this really true, by the way?). This +means there must be some way of selecting which of the many possible adapters +is in fact *the* SMBus. For now, I will ignore this problem. Later on, +we can add a hook somewhere in the i2c module to help us decide this. + + +A driver, client and adapter +------------------- + +We will not need to extend i2c_driver, i2c_client or i2c_adapter. This means +that struct smbus_driver is exactly the same as struct i2c_driver, and +struct smbus_client is the same as struct i2c_client, and smbus_adapter is +the same as struct i2c_adapter. We *will* define the smbus_* variants, and +use them within this module, so it should be easy to extend them after all. + +Note that at this moment, 10 bit SMBus addresses seem to be only +partially supported by the i2c module. We will ignore this issue for +now. + + +An algorithm +------------ + +struct smbus_algorithm { + char name[32]; + unsigned int id; + int (* master_xfer) (struct smbus_adapter *adap, struct smbus_msg msgs[], + int num); + int (* slave_send) (struct smbus_adapter *,char *, int); + int (* slave_recv) (struct smbus_adapter *,char *, int); + int (* algo_control) (struct smbus_adapter *, unsigned int, unsigned long); + int (* client_register) (struct smbus_client *); + int (* client_unregister) (struct smbus_client *); + + int (* smbus_access) (struct smbus_adapter *, __u8 addr, char read_write, + __u8 command, int size, union smbus_data * data); +} + + +A description of the above struct: + smbus_access: A function to access the SMBus. If the algorithm describes + a i2c bus, it will use master_xfer to simulate this operation. In + case of a PIIX4, master_xfer, slave_send and slave_recv will not be + implemented. + + +Access functions +---------------- + +All these functions are defined extern. + +The i2c access function should not be used within SMBus chip drivers, as +they might not be defined (for the PIIX4, for example). Instead, use the +following general access function, or one of the easier functions based +on it: + +int smbus_access (struct smbus_adapter *, __u8 addr, char read_write, + __u8 command, int size, union smbus_data * data); + +There will be specific SMBus registration functions too, like the i2c +ones. + + +Module sensors.o +================ + +This module acts as a coordinations point between specific sensor modules +(which each support a certain kind of sensor). We need this module to unite +SMBus access and ISA access. + + +A driver or adapter +------------------- + +We will not need to extend smbus_driver or smbus_adapter. This means that +struct sensor_driver is exactly the same as struct smbus_driver, and struct +sensor_adapter is the same as struct smbus_adapter. We *will* define the +sensor_* variants, and use them within this module, so it should be easy to +extend them after all. + +Note that a driver can be for a chip on either the ISA bus or the +I2C/SMBus. If a specific chip can be on both, you must check variable +client->adapter->algorithm->on_isa to determine which bus you need to access. + + +A client +-------- + +struct sensor_client { + char name[32]; + int id; + unsigned int flags; + unsigned char addr; + struct sensor_adapter *adapter; + struct sensor_driver *driver; + void *data; + + unsigned int full_address; +} + +A client is a specific sensor chip. Its operation is controlled by a driver +(which describes a type of sensor chip), and it is connected to an adapter +(a bus, either a I2C/SMBus or the ISA bus). + +A description of the above struct: + full_address: The full client address. ISA addresses and 10-bit SMBus + addresses do not fit in the i2c-compatible addr field, so we needed + a new field. + + + +An algorithm +------------ + +struct sensor_algorithm { + char name[32]; + unsigned int id; + int (* master_xfer) (struct sensor_adapter *adap, struct smbus_msg msgs[], + int num); + int (* slave_send) (struct sensor_adapter *,char *, int); + int (* slave_recv) (struct sensor_adapter *,char *, int); + int (* algo_control) (struct sensor_adapter *, unsigned int, unsigned long); + int (* client_register) (struct sensor_client *); + int (* client_unregister) (struct sensor_client *); + + int (* smbus_access) (struct sensor_adapter *, __u8 addr, char read_write, + __u8 command, int size, union smbus_data * data); + int isa_bus; +} + +A description of the above struct: + isa_bus: 0 if this structure describes SMBus access, 1 if it describes + ISA access. + +In case of the ISA bus, the master_xfer, slave_send, slave_recv and +smbus_access hooks will be NULL, because these functions make no sense. +It is regrettably not easy to create an access abstraction in which both +ISA bus access and SMBus access are united. See below for examples how +you can solve this problem. + + +Access functions +---------------- + +All these functions are defined extern. + +The most imporant additional access function: + + int is_on_isa (struct sensor_client *); + +This function tells us whether a specific client is connected to the ISA +bus or to the SMBus. This is important, because it determines whether we +can use the SMBus access routines. + +As an example, I will here implement our old LM78 access function: + +u8 lm78_read_value(struct sensor_client *client, u8 register) +{ + if (is_on_isa(client)) { + /* Ignore the check for LM78_BUSY to keep things simple here; the best + place to put this semaphore struct would be in client->data */ + outb_p(register,client->address + LM78_ADDR_REG_OFFSET); + return inb_p(client->address + LM78_DATA_REG_OFFSET); + } else + return smbus_read_byte_data(client,register); + /* This is a standard function based on smbus_access */ +} + + diff --git a/doc/useful_addresses.html b/doc/useful_addresses.html new file mode 100644 index 00000000..ec85655f --- /dev/null +++ b/doc/useful_addresses.html @@ -0,0 +1,60 @@ + + + +I²C, SMBus and hardware monitoring references + + +

I²C, SMBus and hardware monitoring references

+This document contains all kinds of useful references to I²C and SMBus +documents and implementations, and to hardware sensors documentation and +implementations. Please report any other references you have found! + + + diff --git a/i2c/Makefile b/i2c/Makefile new file mode 100644 index 00000000..ed1a7663 --- /dev/null +++ b/i2c/Makefile @@ -0,0 +1,40 @@ +################################################# +# config +KERNEL_LOCATION=/usr/src/linux +CURRENT=$(shell uname -r) + +################################################# +# some magic for using linux kernel settings +# when compiling module(s) + +MOD_SUB_DIRS = drivers +#MX_OBJS = i2c-core.o algo-bit.o i2c-dev.o bit-lp.o bit-velle.o algo-pcf.o +MX_OBJS = i2c-core.o algo-bit.o i2c-dev.o + +M_OBJS = bit-lp.o bit-velle.o bit-mb.o +EXTRA_CFLAGS = -Wno-unused + +here: + DIR=`pwd`; (cd $(KERNEL_LOCATION); make SUBDIRS=$$DIR modules) + +install: + su -c "cp -v $(M_OBJS) $(MX_OBJS) /lib/modules/$(CURRENT)/misc" + +clean: + -rm -f $(M_OBJS) $(MX_OBJS) .*.o.flags *~ + +up: + insmod i2c-core.o i2c_debug=0 + insmod algo-bit.o bit_test=1 i2c_debug=1 + insmod bit-lp.o base=0x278 + insmod i2c-dev.o + insmod drivers/eeprom.o test=1 + +down: + -rmmod eeprom + -rmmod i2c-dev + -rmmod bit-lp + -rmmod algo-bit + -rmmod i2c-core + +include $(KERNEL_LOCATION)/Rules.make diff --git a/i2c/README b/i2c/README new file mode 100644 index 00000000..b87e646a --- /dev/null +++ b/i2c/README @@ -0,0 +1,22 @@ +Okay, a new version is born... +There are still many files from the old version lying around that +need to be ported - anyone have spare time ? + +Currently, the following modules are running in the new mode: + i2c.c - main admin module & common interface + i2c-dev.c - character device interface for the bus + algo-bit.c - Algorithms for bit banging + bit-lp.c - hw part for parallel port adapter, Philips interface + bit-velle.c - hw part for parallel port adapter, Vellemann K9000 + bit-mb - VIA Chipset motherboards' SMBus -> see README.bitmb + +The other modules, esp. algo-pcf.c still need to be converted. For the +hw specific mods, this should be relatively easy & quick. + +A simple driver example is in drivers -- a piece of code to access EEPROMs. + +Simon (simon@tk.uni-linz.ac.at) + +For more detailed documentation have a look at +http://www.tk.uni-linz.ac.at/~simon/private/i2c + diff --git a/i2c/README.bitmb b/i2c/README.bitmb new file mode 100644 index 00000000..d739e86b --- /dev/null +++ b/i2c/README.bitmb @@ -0,0 +1,36 @@ + + README for bit-mb.c and hw-monitor.c + September 23, 1998. Kyösti Mälkki + + The questions you would probably want to ask + about bit-mb and hw-monitor, here are the answers. + +What are bit-mb.c and hw-monitor.c? + + Both are new modules for Simon G. Vogl's i2c-driver for Linux: + + Bit-mb gives you access to motherboard's SMBus (or i2c -bus). + You should have VIA VT82C586B South Bridge, and this is verified on + module-load. On some motherboards, you must enable USB -support in BIOS-setup + to make it active, check this with pciutils or in /proc/pci. + NOTE: Accessing wrong registers on PCI-chipset is likely to lock your + computer. If your hardware goes BOOOMMM, don't blame me. + + Hw-monitor is an i2c-driver for GL518SM (by Genesys Logic) chip, used in my + cpu fan (FAN78, EISCA-compatible). This gives you /proc/temperature to show + your cpu temperature. It is not tested with FAN79 (GL520SM), I will get the + datasheet for that in a few days. + + +Problems? + + This is not near alpha-level, but it works with my FIC-VA503+. + There is no quarantee your motherboard manufacturer has used the VIA + recommended pins for i2c-lines... With my FIC, they have. + + For error reports, turn i2c-debugging on. Check Simon's sourcecode + files for how to do that. + + Please report any success or failure with possible logfiles to me: + + Kyösti Mälkki kmalkki@cc.hut.fi diff --git a/i2c/algo-bit.c b/i2c/algo-bit.c new file mode 100644 index 00000000..bea9d109 --- /dev/null +++ b/i2c/algo-bit.c @@ -0,0 +1,665 @@ +/* ------------------------------------------------------------------------- */ +/* adap-bit.c i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ +static char alg_rcsid[] = "$Id: algo-bit.c,v 1.7 1998/09/28 06:45:38 i2c Exp i2c $"; + +#include +#include +#include +#include +#include + + +#if LINUX_VERSION_CODE >= 0x020100 +# include +#else +# include +#endif + + +#include +#include +#include + +#include "i2c.h" +#include "algo-bit.h" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; +#define DEBSTAT(x) if (i2c_debug>=3) x; /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) x; + /* debug the protocol by showing transferred bits */ + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#if LINUX_VERSION_CODE >= 0x02016e +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#else +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +#endif +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +int i2c_debug=1; +int bit_test=0; /* see if the line-setting functions work */ +int bit_scan=0; /* have a look at what's hanging 'round */ + +/* + * This array contains the hw-specific functions for + * each port (hardware) type. + */ +struct bit_adapter *bit_adaps[BIT_ADAP_MAX]; +int adap_count; +struct i2c_adapter *i2c_adaps[BIT_ADAP_MAX]; + +/* --- setting states on the bus with the right timing: --------------- */ + +#define setsda(adap,val) adap->setsda(adap->data, val) +#define setscl(adap,val) adap->setscl(adap->data, val) +#define getsda(adap) adap->getsda(adap->data) +#define getscl(adap) adap->getscl(adap->data) + +inline void sdalo(struct bit_adapter *adap) +{ + setsda(adap,0); + udelay(adap->udelay); +} + +inline void sdahi(struct bit_adapter *adap) +{ + setsda(adap,1); + udelay(adap->udelay); +} + +inline void scllo(struct bit_adapter *adap) +{ + setscl(adap,0); + udelay(adap->udelay); +#ifdef SLO_IO + SLO_IO +#endif +} + +/* + * Raise scl line, and do checking for delays. This is necessary for slower + * devices. + */ +inline int sclhi(struct bit_adapter *adap) +{ + int start=jiffies; + + setscl(adap,1); + + udelay(adap->udelay); + if (adap->getscl == NULL ) + return 0; + while (! getscl(adap) ) { + /* the hw knows how to read the clock line, + * so we wait until it actually gets high. + * This is safer as some chips may hold it low + * while they are processing data internally. + */ + setscl(adap,1); + if (start+adap->timeout <= jiffies) { + return -ETIMEDOUT; + } +#if LINUX_VERSION_CODE >= 0x02016e + if (current->need_resched) + schedule(); +#else + if (need_resched) + schedule(); +#endif + } + DEBSTAT(printk("needed %ld jiffies\n", jiffies-start)); +#ifdef SLO_IO + SLO_IO +#endif + return 0; +} + + +/* --- other auxiliary functions -------------------------------------- */ +void i2c_start(struct bit_adapter *adap) +{ + /* assert: scl, sda are high */ + DEBPROTO(printk("S ")); + sdalo(adap); + scllo(adap); +} + +void i2c_repstart(struct bit_adapter *adap) +{ + /* scl, sda may not be high */ + DEBPROTO(printk(" Sr ")); + setsda(adap,1); + setscl(adap,1); + udelay(adap->udelay); + + sdalo(adap); + scllo(adap); +} + + +void i2c_stop(struct bit_adapter *adap) +{ + DEBPROTO(printk("P\n")); + /* assert: scl is low */ + sdalo(adap); + sclhi(adap); + sdahi(adap); +} + +/* send a byte without start cond., look for arbitration, + check ackn. from slave */ +/* return 1 if ok */ +int i2c_outb(struct bit_adapter *adap, char c) +{ + int i; + int sb; + int ack; + + /* assert: scl is low */ + DEB2(printk(" i2c_outb:%2.2X\n",c&0xff)); + for ( i=7 ; i>=0 ; i-- ) { + sb = c & ( 1 << i ); + setsda(adap,sb); + udelay(adap->udelay); + DEBPROTO(printk("%d",sb!=0)); + if (sclhi(adap)<0) { /* timed out */ + sdahi(adap); /* we don't want to block the net */ + return -ETIMEDOUT; + }; + /* do arbitration here: + * if ( sb && ! getsda(adap) ) -> ouch! Get out of here. + */ + setscl( adap, 0 ); + udelay(adap->udelay); + } + sdahi(adap); + if (sclhi(adap)<0){ /* timeout */ + return -ETIMEDOUT; + }; + /* read ack: SDA should be pulled down by slave */ + ack=getsda(adap); /* ack: sda is pulled low ->success. */ + DEB2(printk(" i2c_outb: getsda() = 0x%2.2x\n", ~ack )); + + DEBPROTO( printk("[%2.2x]",c&0xff) ); + DEBPROTO(if (0==ack) printk(" A "); else printk(" NA ") ); + scllo(adap); + return 0==ack; /* return 1 if device acked */ + /* assert: scl is low (sda undef) */ +} + + + +int i2c_inb(struct bit_adapter *adap) +{ + /* read byte via i2c port, without start/stop sequence */ + /* acknowledge is sent in i2c_read. */ + int i; + char indata; + + /* assert: scl is low */ + DEB2(printk("i2c_inb.\n")); + + sdahi(adap); + indata=0; + for (i=0;i<8;i++) { + if (sclhi(adap)<0) { /* timeout */ + return -ETIMEDOUT; + }; + indata *= 2; + if ( getsda(adap) ) + indata |= 0x01; + scllo(adap); + } + /* assert: scl is low */ + DEBPROTO(printk(" %2.2x", indata & 0xff)); + return (int) (indata & 0xff); +} + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +int test_bus(struct bit_adapter *adap) { + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("i2c(bit): Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("i2c(bit): Adapter: %s scl: %d sda: %d -- testing...\n", + adap->name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("i2c(bit): %s seems to be busy.\n",adap->name); + goto bailout; + } + sdalo(adap); + printk("i2c(bit):1 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("i2c(bit): %s SDA stuck high!\n",adap->name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c(bit): %s SCL unexpected low while pulling SDA low!\n", + adap->name); + goto bailout; + } + sdahi(adap); + printk("i2c(bit):2 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("i2c(bit): %s SDA stuck low!\n",adap->name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c(bit): %s SCL unexpected low while SDA high!\n",adap->name); + goto bailout; + } + scllo(adap); + printk("i2c(bit):3 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 != getscl(adap) ) { + printk("i2c(bit): %s SCL stuck high!\n",adap->name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c(bit): %s SDA unexpected low while pulling SCL low!\n", + adap->name); + goto bailout; + } + sclhi(adap); + printk("i2c(bit):4 scl: %d sda: %d \n",getscl(adap),getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("i2c(bit): %s SCL stuck low!\n",adap->name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c(bit): %s SDA unexpected low while SCL high!\n", + adap->name); + goto bailout; + } + printk("i2c(bit): %s passed test.\n",adap->name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +} + +/* ----- Utility functions + */ + +inline int try_address(struct bit_adapter *adap, + unsigned char addr, int retries) +{ + int i,ret = -1; + for (i=0;iudelay*/); + i2c_start(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("i2c(bit): needed %d retries for %d\n",i,addr)); + return ret; +} + +int sendbytes(struct bit_adapter *adap,const char *buf, int count) +{ + char c; + const char *temp = buf; + int retval; + int wrcount=0; + + while (count > 0) { + c = *temp; + DEB2(printk("i2c(bit): %s i2c_write: writing %2.2X\n", + adap->name, c&0xff)); + retval = i2c_outb(adap,c); + if (retval>0) { + count--; + temp++; + wrcount++; + } else { /* arbitration or no acknowledge */ + printk("i2c(bit): %s i2c_write: error - bailout.\n", + adap->name); + i2c_stop(adap); + return -EREMOTEIO; /* got a better one ?? */ + } +#if 0 + /* from asm/delay.h */ + __delay(adap->mdelay * (loops_per_sec / 1000) ); +#endif + } + return wrcount; +} + +inline int readbytes(struct bit_adapter *adap,char *buf,int count) +{ + char *temp = buf; + int inval; + int rdcount=0; /* counts bytes read */ + + while (count > 0) { + inval = i2c_inb(adap); +/*printk("%#02x ",inval); +if ( ! (count % 16) ) +printk("\n"); +*/ + if (inval>=0) { + *temp = inval; + rdcount++; + } else { /* read timed out */ + printk("i2c(bit): i2c_read: i2c_inb timed out.\n"); + break; + } + + if ( count > 1 ) { /* send ack */ + sdalo(adap); + DEBPROTO(printk(" Am ")); + } else { + sdahi(adap); /* neg. ack on last byte */ + DEBPROTO(printk(" NAm ")); + } + if (sclhi(adap)<0) { /* timeout */ + sdahi(adap); + printk("i2c(bit): i2c_read: Timeout at ack\n"); + return -ETIMEDOUT; + }; + scllo(adap); + sdahi(adap); + temp++; + count--; + } + return rdcount; +} + +inline int bit_doAddress(struct bit_adapter *adap, struct i2c_msg *msg, + int retries) +{ + unsigned short flags = msg->flags; + unsigned char addr; + int ret; + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | ( flags & I2C_M_TENMASK ); + DEB2(printk("addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + ret = i2c_outb(adap,msg->addr); + if (ret != 1) { + printk("died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + ret = try_address(adap, addr, retries); + if (ret!=1) { + return -EREMOTEIO; + } + } + return 0; +} + +int bit_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bit_adapter *adap = (struct bit_adapter*)adapter->data; + struct i2c_msg *pmsg; + int i,ret; + + i2c_start(adap); + for (i=0;iretries); + if (ret != 0) { + DEB2(printk("i2c(bit): NAK from device adr %#2x msg #%d\n" + ,msgs[i].addr,i)); + return -EREMOTEIO; + } + if (pmsg->flags & I2C_M_RD ) { + /* read bytes into buffer*/ + ret = readbytes(adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c(bit): read %d bytes.\n",ret)); + } else { + /* write bytes from buffer */ + ret = sendbytes(adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c(bit): wrote %d bytes.\n",ret)); + } + if (iadapter; + struct bit_adapter *adap = (struct bit_adapter*)adapter->data; + + if (adap->client_register != NULL) + return adap->client_register(client); + return 0; +} + +int client_unregister(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct bit_adapter *adap = (struct bit_adapter*)adapter->data; + + if (adap->client_unregister != NULL) + return adap->client_unregister(client); + return 0; +} + +/* -----exported algorithm data: ------------------------------------- */ + +struct i2c_algorithm bit_algo = { + "Bit-shift algorithm", + ALGO_BIT, + bit_xfer, +#if 0 + bit_send, /* master_xmit */ + bit_recv, /* master_recv */ + bit_comb, /* master_comb */ +#endif + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + client_register, + client_unregister, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_bit_add_bus(struct bit_adapter *adap) +{ + int i,ack; + struct i2c_adapter *i2c_adap; + + for (i = 0; i < BIT_ADAP_MAX; i++) + if (NULL == bit_adaps[i]) + break; + if (BIT_ADAP_MAX == i) + return -ENOMEM; + + if (bit_test) { + int ret = test_bus(adap); + if (ret<0) + return -ENODEV; + } + i2c_adap = kmalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (i2c_adap == NULL) + return -ENOMEM; + + bit_adaps[i] = adap; + adap_count++; + DEB2(printk("i2c(bit): hw routines for %s registered.\n",adap->name)); + + /* register new adapter to i2c module... */ + + memset(i2c_adap,0,sizeof(struct i2c_adapter)); + strcpy(i2c_adap->name,adap->name); + i2c_adap->id = bit_algo.id | adap->id; + i2c_adap->algo = &bit_algo; + i2c_adap->data = adap; + i2c_adap->timeout = 100; /* default values, should */ + i2c_adap->retries = 3; /* be replaced by defines */ + i2c_adaps[i] = i2c_adap; + i2c_add_adapter(i2c_adap); + + /* scan bus */ + if (bit_scan) { + printk(KERN_INFO " i2c(bit): scanning bus %s.\n", adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_start(adap); + ack = i2c_outb(adap,i); + i2c_stop(adap); + if (ack>0) { + printk("(%02x)",i>>1); + } else + printk("."); + } + printk("\n"); + } + return 0; +} + + +int i2c_bit_del_bus(struct bit_adapter *adap) +{ + int i; + + for (i = 0; i < BIT_ADAP_MAX; i++) + if ( adap == bit_adaps[i]) + break; + if ( BIT_ADAP_MAX == i) { + printk(KERN_WARNING " i2c(bit): could not unregister bus: %s\n", + adap->name); + return -ENODEV; + } + + bit_adaps[i] = NULL; + i2c_del_adapter(i2c_adaps[i]); + kfree(i2c_adaps[i]); + i2c_adaps[i] = NULL; + adap_count--; + DEB2(printk("i2c(bit): adapter unregistered: %s\n",adap->name)); + + return 0; +} + +int algo_bit_init (void) +{ + int i; + + for (i=0;i"); +MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); + +MODULE_PARM(bit_test, "i"); +MODULE_PARM(bit_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck"); +MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug,"debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol"); + + +EXPORT_SYMBOL(i2c_bit_add_bus); +EXPORT_SYMBOL(i2c_bit_del_bus); + + +int init_module(void) +{ + return algo_bit_init(); +} + +void cleanup_module(void) +{ + i2c_del_algorithm(&bit_algo); +} +#endif + + + + + + + + + + diff --git a/i2c/algo-bit.h b/i2c/algo-bit.h new file mode 100644 index 00000000..f37ce492 --- /dev/null +++ b/i2c/algo-bit.h @@ -0,0 +1,38 @@ +#ifndef ALGO_BIT_H +#define AGLO_BIT_H 1 + +/* --- Defines for bit-adapters --------------------------------------- */ +#include "i2c.h" +/* + * This struct contains the hw-dependent functions of bit-style adapters to + * manipulate the line states, and to init any hw-specific features. This is + * only used if you have more than one hw-type of adapter running. + */ +struct bit_adapter { + char name[32]; /* give it a nice name */ + unsigned int id; /* not used yet, maybe later */ + void *data; /* private data for lolevel routines */ + void (*setsda) (void *data, int state); + void (*setscl) (void *data, int state); + int (*getsda) (void *data); + int (*getscl) (void *data); + + /* administrative calls */ + int (*client_register)(struct i2c_client *); + int (*client_unregister)(struct i2c_client *); + + /* local settings */ + int udelay; + int mdelay; + int timeout; + +}; + +extern struct bit_adapter *bit_adaps[]; + +#define BIT_ADAP_MAX 16 + +int i2c_bit_add_bus(struct bit_adapter *); +int i2c_bit_del_bus(struct bit_adapter *); + +#endif /* ALGO_BIT_H */ diff --git a/i2c/bit-elv.c b/i2c/bit-elv.c new file mode 100644 index 00000000..ea6af0ba --- /dev/null +++ b/i2c/bit-elv.c @@ -0,0 +1,146 @@ +/* ------------------------------------------------------------------------- */ +/* bit-elv.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ +static char rcsid[] = "$Id: bit-elv.c,v 1.1 1998/09/05 18:20:09 i2c Exp $"; +/* + * $Log: bit-elv.c,v $ + * Revision 1.1 1998/09/05 18:20:09 i2c + * Initial revision + * + * Revision 1.3 1998/01/20 10:01:29 i2c + * *** empty log message *** + * + * Revision 1.2 1997/06/15 14:21:37 i2c + * *** empty log message *** + * + * + */ + +#include +#include +#include +#include +#include +#include "i2c.h" +#include "i2c-priv.h" + + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ +#define DEBINIT(x) x /* detection status messages */ +/* ----- local functions ---------------------------------------------- */ +#if (BITADAPS) == 1 +#define Local inline +#define bit_elv_getscl getscl +#define bit_elv_setscl setscl +#define bit_elv_getsda getsda +#define bit_elv_setsda setsda +#define bit_elv_bit_init bit_init +#define bit_elv_bit_exit bit_exit +#else +#define Local static +#endif + +#define portData(minor) ((char)((int)i2c_table[minor].minor_data)) + +Local void bit_elv_setscl(int minor, int state) +{ + if (state) { + portData(minor)&=0xfe; + } else { + portData(minor)|=1; + } + outb(portData(minor), DATA); +} + +Local void bit_elv_setsda(int minor, int state) +{ + if (state) { + portData(minor)&=0xfd; + } else { + portData(minor)|=2; + } + outb(portData(minor), DATA); +} + +Local int bit_elv_getscl(int minor) +{ + return ( 0 == ( (inb_p(STAT)) & 0x08 ) ); +} + +Local int bit_elv_getsda(int minor) +{ + return ( 0 == ( (inb_p(STAT)) & 0x40 ) ); +} + +Local int bit_elv_bit_init(int minor) +{ + if (check_region(i2c_table[minor].base, + (i2c_table[minor].base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + /* test for ELV adap. */ + if (inb(STAT) & 0x80) { /* BUSY should be high */ + DEBINIT(printk("i2c ELV: Busy was low.\n")); + return -ENODEV; + } else { + outb(0x04,CTRL); /* SLCT auf low */ + udelay(40); + if ( inb(STAT) && 0x10 ) { + outb(0x0c,CTRL); + DEBINIT(printk("i2c ELV: Select was high.\n")); + return -ENODEV; + } + } + request_region(i2c_table[minor].base, + (i2c_table[minor].base == 0x3bc)? 3 : 8, + "i2c (ELV adapter)"); + portData(minor) = 0; + outb(0,DATA); + } + DEBINIT(printk("i2c%d: scl: %d sda %d \n",minor, + bit_elv_getscl(minor),bit_elv_getsda(minor)) ); + return 0; +} + +Local void bit_elv_bit_exit(int minor) +{ + outb(0,DATA); + outb(0x0c,CTRL); + release_region( i2c_table[minor].base , + (i2c_table[minor].base == 0x3bc)? 3 : 8 ); +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +#if (BITADAPS) > 1 +struct i2c_bit_opns bit_elv_ops = { + bit_elv_setscl, + bit_elv_setsda, + bit_elv_getscl, + bit_elv_getsda, + bit_elv_bit_init, + bit_elv_bit_exit +}; +#endif diff --git a/i2c/bit-lp.c b/i2c/bit-lp.c new file mode 100644 index 00000000..55c438a5 --- /dev/null +++ b/i2c/bit-lp.c @@ -0,0 +1,188 @@ +/* ------------------------------------------------------------------------- */ +/* bit-lp.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ +static char rcsid[] = "$Id: bit-lp.c,v 1.5 1998/09/28 06:45:38 i2c Exp i2c $"; +/* ------------------------------------------------------------------------- */ + +#include +#include +#include +#include + +#include "i2c.h" +#include "algo-bit.h" + +#define DEFAULT_BASE 0x378 +int base=0; + +/* Note: all we need to know is the base address of the parallel port, so + * instead of having a dedicated struct to store this value, we store this + * int in the pointer field (=bit_lp_ops.data) itself. + */ + +/* Note2: as the hw that implements the i2c bus on the parallel port is + * incompatible with other epp stuff etc., we access the port exclusively + * and don't cooperate with parport functions. + */ + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + +/* ----- printer port defines ------------------------------------------*/ + /* Pin Port Inverted name */ +#define I2C_ON 0x20 /* 12 status N paper */ + /* ... only for phil. not used */ +#define I2C_SDA 0x80 /* 9 data N data7 */ +#define I2C_SCL 0x08 /* 17 ctrl N dsel */ + +#define I2C_SDAIN 0x80 /* 11 stat Y busy */ +#define I2C_SCLIN 0x08 /* 15 stat Y enable */ + +#define I2C_DMASK 0x7f +#define I2C_CMASK 0xf7 + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + +/* ----- local functions ---------------------------------------------- */ + +void bit_lp_setscl(void *data, int state) +{ + /*be cautious about state of the control register - + touch only the one bit needed*/ + if (state) { + outb(inb(CTRL)|I2C_SCL, CTRL); + } else { + outb(inb(CTRL)&I2C_CMASK, CTRL); + } +} + +void bit_lp_setsda(void *data, int state) +{ + if (state) { + outb(I2C_DMASK , DATA); + } else { + outb(I2C_SDA , DATA); + } +} + +int bit_lp_getscl(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SCLIN ) ); +} + +int bit_lp_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +int bit_lp_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + request_region(base,(base == 0x3bc)? 3 : 8, + "i2c (parallel port adapter)"); + /* reset hardware to sane state */ + bit_lp_setsda((void*)base,1); + bit_lp_setscl((void*)base,1); + } + return 0; +} + +void bit_lp_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + +int bit_lp_reg(struct i2c_client *client) +{ + MOD_INC_USE_COUNT; + return 0; +} + +int bit_lp_unreg(struct i2c_client *client) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +struct bit_adapter bit_lp_ops = { + "Philips Parallel port adapter", + HW_B_LP, + NULL, + bit_lp_setsda, + bit_lp_setscl, + bit_lp_getsda, + bit_lp_getscl, + bit_lp_reg, + bit_lp_unreg, + 80, 80, 100, /* waits, timeout */ +}; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Philips parallel port adapter"); + +MODULE_PARM(base, "i"); + +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_lp_ops.data=(void*)DEFAULT_BASE; + if (bit_lp_init()==0) { + i2c_bit_add_bus(&bit_lp_ops); + } else { + return -ENODEV; + } + } else { + bit_lp_ops.data=(void*)base; + if (bit_lp_init()==0) { + i2c_bit_add_bus(&bit_lp_ops); + } else { + return -ENODEV; + } + } + printk("bit_lp: found device at %#x.\n",base); + return 0; +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_lp_ops); + bit_lp_exit(); +} + +#endif + + + diff --git a/i2c/bit-mb.c b/i2c/bit-mb.c new file mode 100644 index 00000000..4f3371b7 --- /dev/null +++ b/i2c/bit-mb.c @@ -0,0 +1,163 @@ +/* + Motherboard SMBus (or i2c) Support for VIA VT82C586B South Bridge + Based on bit-lp.c by Simon Vogl + + Copyright (c) 1998 Kyösti Mälkki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include + +#include "i2c.h" +#include "algo-bit.h" + +/* Power management registers */ +#define PM_IO_BASE pm_io_base +#define PM_IO_BASE_REGISTER 0x48 +#define I2C_DIR (PM_IO_BASE+0x40) +#define I2C_OUT (PM_IO_BASE+0x42) +#define I2C_IN (PM_IO_BASE+0x44) +#define I2C_SCL 0x02 +#define I2C_SDA 0x04 + +/* io-region reservation */ +#define IOSPACE 0x06 +#define IOTEXT "Via VT82C586B i2c" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + +/* ----- local functions ---------------------------------------------- */ + +u32 pm_io_base; + +static void bit_mb_setscl(void *data, int state) +{ + outb(state ? inb(I2C_OUT)|I2C_SCL : inb(I2C_OUT)&~I2C_SCL, I2C_OUT); +} + +static void bit_mb_setsda(void *data, int state) +{ + outb(state ? inb(I2C_OUT)|I2C_SDA : inb(I2C_OUT)&~I2C_SDA, I2C_OUT); +} + +static int bit_mb_getscl(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SCL) ); +} + +static int bit_mb_getsda(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SDA) ); +} + +static int bit_mb_reg(struct i2c_client *client) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int bit_mb_unreg(struct i2c_client *client) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* ------------------------------------------------------------------------ */ + +struct bit_adapter bit_mb_ops = { + "VT82C586B i2c", + HW_B_MB, + NULL, + bit_mb_setsda, + bit_mb_setscl, + bit_mb_getsda, + bit_mb_getscl, + bit_mb_reg, + bit_mb_unreg, + 10, 10, 100, /*waits, timeout */ +}; + +u32 pm_io_base=0; + +static int bit_mb_init(void) +{ + if (check_region(I2C_DIR, IOSPACE) < 0 ) { + return -ENODEV; + } else { + request_region(I2C_DIR, IOSPACE, IOTEXT); + + /* Set lines to output and make them high */ + outb(inb(I2C_DIR) | I2C_SCL | I2C_SDA, I2C_DIR); + bit_mb_setsda(NULL, 1); + bit_mb_setscl(NULL, 1); + } + return 0; +} + +static void bit_mb_exit(void) +{ + release_region(I2C_DIR, IOSPACE); +} + +static u32 find_i2c(void) +{ + struct pci_dev *s_bridge; + u32 val; + + if (! pci_present()) + return(0); + + s_bridge = pci_find_device( + PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, NULL); + if (! s_bridge) return 0; + + if ( PCIBIOS_SUCCESSFUL != + pci_read_config_dword(s_bridge, PM_IO_BASE_REGISTER, &val) ) + return 0; + + return (val & (0xff<<8)); +} + +#ifdef MODULE +MODULE_AUTHOR("Kyosti Malkki "); +MODULE_DESCRIPTION("SMBus for VT82C586"); + +int init_module(void) +{ + pm_io_base = find_i2c(); + if (pm_io_base == 0) return -ENODEV; + if (bit_mb_init()==0) { + i2c_bit_add_bus(&bit_mb_ops); + } else { + return -ENODEV; + } + printk("Using i2c bus on motherboard\n"); + return 0; +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_mb_ops); + bit_mb_exit(); +} +#endif diff --git a/i2c/bit-velle.c b/i2c/bit-velle.c new file mode 100644 index 00000000..ffababcb --- /dev/null +++ b/i2c/bit-velle.c @@ -0,0 +1,195 @@ +/* ------------------------------------------------------------------------- */ +/* bit-velle.c i2c-hw access for Velleman K9000 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-96 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ +static char rcsid[] = "$Id: bit-velle.c,v 1.3 1998/09/15 18:50:04 i2c Exp $"; +/* + * $Log: bit-velle.c,v $ + * Revision 1.3 1998/09/15 18:50:04 i2c + * *** empty log message *** + * + * Revision 1.2 1998/09/13 16:54:55 i2c + * *** empty log message *** + * + * Revision 1.1 1998/07/29 08:09:35 i2c + * Initial revision + * + * Revision 1.1 1998/01/20 10:01:29 i2c + * Initial revision + * + * Revision 1.1 1996/11/17 11:00:03 i2c + * Initial revision + * + * + */ + +#include +#include +#include +#include /* for 2.0 kernels to get NULL */ +#include /* for 2.0 kernels to get ENODEV */ +#include + +#include "i2c.h" +#include "algo-bit.h" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + + /* Pin Port Inverted name */ +#define I2C_SDA 0x02 /* ctrl bit 1 (inv) */ +#define I2C_SCL 0x08 /* ctrl bit 3 (inv) */ + +#define I2C_SDAIN 0x10 /* stat bit 4 */ +#define I2C_SCLIN 0x08 /* ctrl bit 3 (inv) (reads own output) */ + +#define I2C_DMASK 0xfd +#define I2C_CMASK 0xf7 + + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + +#define DEFAULT_BASE 0x378 +int base=0; + +/* ----- local functions --------------------------------------------------- */ + +void bit_velle_setscl(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_CMASK, CTRL); + } else { + outb(inb(CTRL) | I2C_SCL, CTRL); + } + +} + +void bit_velle_setsda(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_DMASK , CTRL); + } else { + outb(inb(CTRL) | I2C_SDA, CTRL); + } + +} + +int bit_velle_getscl(void *data) +{ + return ( 0 == ( (inb(CTRL)) & I2C_SCLIN ) ); +} + +int bit_velle_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +int bit_velle_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + DEBE(printk("i2c_init: Port %#x already in use.\n", base)); + return -ENODEV; + } else { + request_region(base, (base == 0x3bc)? 3 : 8, + "i2c (Vellemann adapter)"); + bit_velle_setsda((void*)base,1); + bit_velle_setscl((void*)base,1); + } + return 0; +} + +void bit_velle_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + + +int bit_velle_reg(struct i2c_client *client) +{ + MOD_INC_USE_COUNT; + return 0; +} + +int bit_velle_unreg(struct i2c_client *client) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +struct bit_adapter bit_velle_ops = { + "Philips Parallel port adapter", + HW_B_LP, + NULL, + bit_velle_setsda, + bit_velle_setscl, + bit_velle_getsda, + bit_velle_getscl, + bit_velle_reg, + bit_velle_unreg, + 10, 10, 100, /* waits, timeout */ +}; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K9000 adapter"); + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_velle_ops.data=(void*)DEFAULT_BASE; + if (bit_velle_init()==0) { + i2c_bit_add_bus(&bit_velle_ops); + } else { + return -ENODEV; + } + } else { + bit_velle_ops.data=(void*)base; + if (bit_velle_init()==0) { + i2c_bit_add_bus(&bit_velle_ops); + } else { + return -ENODEV; + } + } + printk("bit_velle: found device at %#x.\n",base); + return 0; +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_velle_ops); + bit_velle_exit(); +} + +#endif + + + diff --git a/i2c/detect/Makefile b/i2c/detect/Makefile new file mode 100644 index 00000000..8c3e33c3 --- /dev/null +++ b/i2c/detect/Makefile @@ -0,0 +1,32 @@ +# $Id: Makefile,v 1.1 1996/12/04 20:40:10 i2c Exp $ +# $Log: Makefile,v $ +# Revision 1.1 1996/12/04 20:40:10 i2c +# Initial revision +# +# + + +I2C_HOME = .. + +#---------------------------------------------------------------------------- +I2C_OPT = -DVERIFY_HEADERS -DSLOW_STARTER +KERNEL_OPT = -D__KERNEL__ -DKERNEL -DLINUX -DMODULE +WARNINGS = -Wall -Wstrict-prototypes +COMPILE = -pipe -fomit-frame-pointer -m486 -O2 \ + -I$(I2C_HOME) -I. +DEBUG = + +SRCS = i2c_main.c alg-illeg.c $(HW_SRCS) +INCLUDES = i2c_priv.h +OBJS = $(SRCS:.c=.o) +HDRS = i2c_priv.h +MOD = i2c + +CFLAGS = $(DEBUG) $(WARNINGS) $(COMPILE) $(I2C_OPT) $(KERNEL_OPT) +SFLAGS = -fverbose-asm $(WARNINGS) $(COMPILE) $(PRT_OPT) $(KERNEL_OPT) + +all: detect + +clean: + -rm -f detect + diff --git a/i2c/detect/detect.c b/i2c/detect/detect.c new file mode 100644 index 00000000..d3863058 --- /dev/null +++ b/i2c/detect/detect.c @@ -0,0 +1,68 @@ +/* ------------------------------------------------------------------------- */ +/* detect -- look who's there. Gets address acks from all devices on the bus.*/ +/* It should not change any values in the peripherals. */ +/* ------------------------------------------------------------------------- */ +static char *rcsid="$Id: detect.c,v 2.2 1996/11/17 22:03:06 i2c Exp $"; + +#include +#include +#include +#include +#include +#include +#include "i2c.h" + +#define Write(a,b,c) err(1,write(a,b,c)) +#define Read(a,b,c) err(0,read(a,b,c)) + +void err(int wr, int d){ + if (d==-1){ + printf("%s error: %s !\n",(wr)?"Write":"Read",strerror(errno)); + } +} +#define ERR(x) printf("return value: %d\n", (x) ); + +int main(int argc, char *argv[]){ + int i,f; + char b[40],c; + char *device = "/dev/i2c0"; + + /* parse options */ + opterr=1; + while ( (c=getopt(argc,argv,"hH?d:")) != -1) { + switch (c){ + case 'd': + if (optarg) + device = optarg; + break; + case 'h': + case 'H': + case '?': + default: + printf("%s tries to detect devices on the i2c-bus. Usage:\n",argv[0]); + printf("-d device device to use\n"); + return 0; + } + } + f=open(device,O_RDWR); + if (f<0){ + perror("detect"); + exit(1); + } + ERR( ioctl(f,I2C_UDELAY,10) ); + ERR( ioctl(f,I2C_MDELAY,0) ); +// ERR( ioctl(f,I2C_RETRIES,3) ); + + for (i=0;i<128;i++){ + ioctl(f,I2C_SLAVE,i); + if (0>read (f,b,0) ) { + putchar('.'); + } else { + printf("\n%d worked! \n",i); + } + usleep(100); + fflush(stdout); + } + putchar('\n'); + return close(f); +} diff --git a/i2c/drivers/Makefile b/i2c/drivers/Makefile new file mode 100644 index 00000000..f074ca3e --- /dev/null +++ b/i2c/drivers/Makefile @@ -0,0 +1,23 @@ +################################################# +# config +KERNEL_LOCATION=/usr/src/linux +CURRENT=$(shell uname -r) + +################################################# +# some magic for using linux kernel settings +# when compiling module(s) + +M_OBJS = eeprom.o hw-monitor.c +MX_OBJS = +EXTRA_CFLAGS = -DVERIFY_HEADERS -DSLOW_STARTER -I.. + +here: + DIR=`pwd`; (cd $(KERNEL_LOCATION); make SUBDIRS=$$DIR modules) + +install: + su -c "cp -v $(M_OBJS) $(MX_OBJS) /lib/modules/$(CURRENT)/misc" + +clean: + -rm -f $(M_OBJS) $(MX_OBJS) .*.o.flags *~ + +include $(KERNEL_LOCATION)/Rules.make diff --git a/i2c/drivers/eeprom.c b/i2c/drivers/eeprom.c new file mode 100644 index 00000000..2b037817 --- /dev/null +++ b/i2c/drivers/eeprom.c @@ -0,0 +1,172 @@ +/* experimental eeprom driver + */ +#include +#include +#include +#include + +#include "i2c.h" /* +++ simon */ +#include "algo-bit.h" + + +int test=0; +/* + * Read the configuration EEPROM + */ +#define EEPROM_ADDR 0x50 + +#define EEPROM_FROM 0x50 +#define EEPROM_TO 0x58 /*exclusive.*/ + +#define MEMSIZE 128 /* default eeprom memory size */ +/* i2c_client identifies a single device that is connected to an + * i2c bus. + */ +struct i2c_driver eepromDriver; + +struct { + int size; /* eeprom memory size*/ + unsigned int flags; + unsigned char *memory; +} eepromData; + +struct i2c_client eepromTmpl = { + "Winnov EEPROM", + 0, + 0, + EEPROM_ADDR, + NULL, /* the adapter we sit on */ + &eepromDriver, /* and our access routines */ + NULL, /* for the clients */ +}; + + +int eeprom_attach(struct i2c_adapter *adap) +{ + struct i2c_client *eeprom; + unsigned char buf[256]; + struct i2c_msg msgs[] = { + {EEPROM_ADDR, 0, 1, "\0", }, + {EEPROM_ADDR, I2C_M_RD, 256, buf, }, + }; + __u8 c=0; + int i; + int ret=0; + + printk("wnv-eeprom: Trying to attach to adapter %s \n",adap->name); + printk("Looking for EEPROMs\n"); + eepromTmpl.adapter = adap; + for (i=EEPROM_FROM;iname); + + kfree(client); + + return 0; +} + +#define LOAD_EEPROM 1 +#if 0 +#define SET_SIZE 0xee01 /* set memory size */ +#define GET_SIZE 0xee02 /* get memory size */ +#define GET_MEM 0xee03 /* load data from eeprom & pass memory pointer */ +#define SET_MEM 0xee04 /* dump memory to eeprom */ +#define OPT +#endif +int eeprom_command(struct i2c_client *client,unsigned int cmd, void *arg) +{ + switch(cmd) { + case LOAD_EEPROM: { +#if 0 + __u8 c=0; + int i, ret,d; + i2c_master_send(client,(unsigned char*)&c,1); + i2c_master_recv(client, (char*)&c,1); + i2c_master_send(client,(unsigned char*)&c,1); + ret = i2c_master_recv(client, (char*)& , + sizeof(struct EEPROM)); + if (ret != sizeof(struct EEPROM)) { + err_msg("wnv-eeprom: could only read %d bytes!\n" + ,ret); + return -1; + } + for (d=0,i=0;ieeprom))[i]; + } + if ((__u8)d != 0xff) { + err_msg("wnv-eeprom: Checksum error in configuration EEPROM\n"); + return -1; + } + info_msg("wnv-eeprom: Identified: %s\n", dev->eeprom.szProduct); +#endif + } break; + default: + printk("eeprom: unknown command %x!\n",cmd); + } + return 0; +} + +struct i2c_driver eepromDriver = { + "Videum EEPROM", + -1, + DF_NOTIFY, + eeprom_attach, + eeprom_detach, + eeprom_command, +}; + +#ifdef MODULE +MODULE_PARM(test,"i"); + +/*EXPORT_NO_SYMBOLS;*/ + +int init_module(void) +{ + /* register drivers to the i2c bus admin. system. + * These get notified upon attachment of adapters & + * in turn create new clients that are connected to + * these adapters... + */ + i2c_add_driver(&eepromDriver); + return 0; +} + +void cleanup_module(void) +{ + i2c_del_driver(&eepromDriver); +} +#endif /* MODULE*/ diff --git a/i2c/eeprom/Makefile b/i2c/eeprom/Makefile new file mode 100644 index 00000000..3d724608 --- /dev/null +++ b/i2c/eeprom/Makefile @@ -0,0 +1,41 @@ +# $Id: Makefile,v 1.1 1996/07/23 20:31:56 i2c Exp $ +# $Log: Makefile,v $ +# Revision 1.1 1996/07/23 20:31:56 i2c +# Initial revision +# + +I2C_HOME = .. + +#---------------------------------------------------------------------------- +I2C_OPT = -DVERIFY_HEADERS -DSLOW_STARTER +WARNINGS = -Wall -Wstrict-prototypes +COMPILE = -pipe -fomit-frame-pointer -m486 -O2 \ + -I$(I2C_HOME) -I. +DEBUG = + +SRCS = i2c_main.c adap-illeg.c adap-phil.c adap-lppcf.c adap-isapcf.c \ +# adap-lpbit.c +OBJS = $(SRCS:.c=.o) +HDRS = i2c_priv.h +MAIN = eeprom + +CFLAGS = $(DEBUG) $(WARNINGS) $(COMPILE) $(I2C_OPT) $(KERNEL_OPT) +SFLAGS = -fverbose-asm $(WARNINGS) $(COMPILE) $(PRT_OPT) $(KERNEL_OPT) + +all: $(MAIN) + +install: $(MAIN) + -@mkdir $(I2C_HOME)/bin + cp $(MAIN) $(I2C_HOME)/bin + +clean: + -rm -f a.out $(OBJS) $(MAIN) core .depend detect + +dep depend .depend: $(HDRS) $(SRCS) + gcc $(CFLAGS) $(SRCS) -M >.depend +# +# include dependency files if they exist +# +#ifeq (.depend,$(wildcard .depend)) +#include .depend +#endif diff --git a/i2c/eeprom/README b/i2c/eeprom/README new file mode 100644 index 00000000..dac71e61 --- /dev/null +++ b/i2c/eeprom/README @@ -0,0 +1,4 @@ +This is a simple demo program that prints out the +first 256 bytes of an EEPROM memory. Adittionally, +you can write a test pattern to the chip. +With -[aA] the chip address may be specified. diff --git a/i2c/eeprom/a b/i2c/eeprom/a new file mode 100644 index 00000000..6b3aa0b0 --- /dev/null +++ b/i2c/eeprom/a @@ -0,0 +1,16 @@ +0000 bd 06 04 00 00 00 00 00 00 00 00 00 07 00 00 00 ................ +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +0020 00 00 00 00 00 00 00 00 00 30 36 00 00 00 00 00 .........06..... +0030 00 00 00 00 00 00 00 00 00 56 69 64 65 75 6d 41 .........VideumA +0040 56 20 2d 20 50 6c 75 67 20 61 6e 64 20 50 6c 61 V...Plug.and.Pla +0050 79 00 00 00 00 00 00 57 69 6e 6e 6f 76 00 00 00 y......Winnov... +0060 00 00 00 00 00 00 00 00 00 00 00 34 35 58 45 37 ...........45XE7 +0070 33 30 30 34 38 30 32 30 00 00 00 00 00 00 00 6d 30048020.......m +0080 bd 06 04 00 00 00 00 00 00 00 00 00 07 00 00 00 ................ +0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +00a0 00 00 00 00 00 00 00 00 00 30 36 00 00 00 00 00 .........06..... +00b0 00 00 00 00 00 00 00 00 00 56 69 64 65 75 6d 41 .........VideumA +00c0 56 20 2d 20 50 6c 75 67 20 61 6e 64 20 50 6c 61 V...Plug.and.Pla +00d0 79 00 00 00 00 00 00 57 69 6e 6e 6f 76 00 00 00 y......Winnov... +00e0 00 00 00 00 00 00 00 00 00 00 00 34 35 58 45 37 ...........45XE7 +00f0 33 30 30 34 38 30 32 30 00 00 00 00 00 00 00 6d 30048020.......m diff --git a/i2c/eeprom/b b/i2c/eeprom/b new file mode 100644 index 00000000..6b3aa0b0 --- /dev/null +++ b/i2c/eeprom/b @@ -0,0 +1,16 @@ +0000 bd 06 04 00 00 00 00 00 00 00 00 00 07 00 00 00 ................ +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +0020 00 00 00 00 00 00 00 00 00 30 36 00 00 00 00 00 .........06..... +0030 00 00 00 00 00 00 00 00 00 56 69 64 65 75 6d 41 .........VideumA +0040 56 20 2d 20 50 6c 75 67 20 61 6e 64 20 50 6c 61 V...Plug.and.Pla +0050 79 00 00 00 00 00 00 57 69 6e 6e 6f 76 00 00 00 y......Winnov... +0060 00 00 00 00 00 00 00 00 00 00 00 34 35 58 45 37 ...........45XE7 +0070 33 30 30 34 38 30 32 30 00 00 00 00 00 00 00 6d 30048020.......m +0080 bd 06 04 00 00 00 00 00 00 00 00 00 07 00 00 00 ................ +0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +00a0 00 00 00 00 00 00 00 00 00 30 36 00 00 00 00 00 .........06..... +00b0 00 00 00 00 00 00 00 00 00 56 69 64 65 75 6d 41 .........VideumA +00c0 56 20 2d 20 50 6c 75 67 20 61 6e 64 20 50 6c 61 V...Plug.and.Pla +00d0 79 00 00 00 00 00 00 57 69 6e 6e 6f 76 00 00 00 y......Winnov... +00e0 00 00 00 00 00 00 00 00 00 00 00 34 35 58 45 37 ...........45XE7 +00f0 33 30 30 34 38 30 32 30 00 00 00 00 00 00 00 6d 30048020.......m diff --git a/i2c/eeprom/eeprom.c b/i2c/eeprom/eeprom.c new file mode 100644 index 00000000..9dd77ec2 --- /dev/null +++ b/i2c/eeprom/eeprom.c @@ -0,0 +1,187 @@ +/* ------------------------------------------------------------------------- */ +/* eeprom -- read eeprom contents */ +/* revamped by Gerd Knorr */ +/* ------------------------------------------------------------------------- */ +static char *rcsid="$Id: eeprom.c,v 1.1 1996/07/23 20:32:12 i2c Exp $"; +/* + * $Log: eeprom.c,v $ + * Revision 1.1 1996/07/23 20:32:12 i2c + * Initial revision + * + * Revision 1.2 1996/07/06 17:20:38 i2c + * rewrote loop + * + * Revision 1.1 1996/03/15 08:55:53 root + * Initial revision + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include "i2c.h" + +char *dev="/dev/i2c0"; +int wr=0, adr=0xa0; + + + +void usage(char *name) +{ + fprintf(stderr,"This is a i2c EEPROM tool\n"); + fprintf(stderr," read data: %s [ options ] > file\n",name); + fprintf(stderr," write data: %s [ options ] -w < file\n",name); + fprintf(stderr,"\n"); + fprintf(stderr,"File format is a hex dump. Without \"-w\" switch\n"); + fprintf(stderr,"only parsing (and printing) of the data is done,\n"); + fprintf(stderr,"writing is skipped.\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"options:\n"); + fprintf(stderr," -d device device to use [%s]\n",dev); + fprintf(stderr," -a addr set slave address [0x%02x]\n",adr); + fprintf(stderr,"\n"); + exit(1); +} + +void dump_buf(unsigned char *buf) +{ + int i,j; + + for (i = 0; i < 256; i += 16) { + printf("%04x ",i); + for (j = i; j < i+16; j++) { + if (!(j%4)) + printf(" "); + printf("%02x ",buf[j]); + } + printf(" "); + for (j = i; j < i+16; j++) + printf("%c",isalnum(buf[j]) ? buf[j] : '.'); + printf("\n"); + } +} + +int parse_buf(unsigned char *buf) +{ + int i,j,n,pos,count; + char line[100]; + + for (i = 0; i < 256; i += 16) { + if (NULL == fgets(line,99,stdin)) { + fprintf(stderr,"unexpected EOF\n"); + return -1; + } + if (1 != sscanf(line,"%x%n",&n,&pos)) { + fprintf(stderr,"addr parse error (%d)\n",i>>4); + return -1; + } + if (n != i) { + fprintf(stderr,"addr mismatch\n"); + return -1; + } + for (j = i; j < i+16; j++) { + if (1 != sscanf(line+pos,"%x%n",&n,&count)) { + fprintf(stderr,"value parse error\n"); + return -1; + } + buf[j] = n; + pos += count; + } + } + return 0; +} + +int +read_buf(int fd, unsigned char *buf) +{ + int i,n=8; + unsigned char addr; + + for (i = 0; i < 256; i += n) { + addr = i; + if (-1 == (write(fd,&addr,1))) { + fprintf(stderr,"write addr %s: %s\n",dev,strerror(errno)); + exit(1); + } + if (-1 == (read(fd,buf+i,n))) { + fprintf(stderr,"read data %s: %s\n",dev,strerror(errno)); + exit(1); + } + } +} +int +write_buf(int fd, unsigned char *buf) +{ + int i,j,n = 8; + unsigned char tmp[17]; + + for (i = 0; i < 256; i += n) { + tmp[0] = i; + for (j = 0; j < n; j++) + tmp[j+1] = buf[i+j]; + if (-1 == (write(fd,tmp,n+1))) { + fprintf(stderr," write data %s: %s\n",dev,strerror(errno)); + exit(1); + } + fprintf(stderr,"*"); + usleep(100000); /* 0.1 sec */ + } +} + +int main(int argc, char *argv[]) +{ + int f,c,rescue; + unsigned char buf[256]; + + /* parse options */ + opterr=1; + while ( (c=getopt(argc,argv,"hrwa:d:")) != -1) { + switch (c){ + case 'r': + rescue=1; + break; + case 'w': + wr=1; + break; + case 'd': + dev = optarg; + break; + case 'a': + adr = strtol(optarg,NULL,0); + break; + case 'h': + default: + usage(argv[0]); + } + } + + if (-1 == (f = open(dev,O_RDWR))) { + fprintf(stderr,"open %s: %s\n",dev,strerror(errno)); + exit(1); + } + ioctl(f,I2C_SLAVE,adr>>1); + memset(buf,0,256); + + if (isatty(fileno(stdin))) { + /* read */ + read_buf(f,buf); + dump_buf(buf); + } else { + /* write */ + if (-1 == parse_buf(buf)) + exit(1); + dump_buf(buf); + if (wr) { + fprintf(stderr,"writing to eeprom now... "); + write_buf(f,buf); + fprintf(stderr," ok\n"); + } + } + + close(f); + exit(0); +} diff --git a/i2c/hw-monitor.c b/i2c/hw-monitor.c new file mode 100644 index 00000000..8ff86ef7 --- /dev/null +++ b/i2c/hw-monitor.c @@ -0,0 +1,182 @@ + /* + EISCA Fan SMBus (or i2c) driver + Based on various char-device sources. + + Copyright (c) 1998 Kyösti Mälkki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c.h" +#include "algo-bit.h" + +/* Fan defines */ +#define I2C_GL518SM 0x2d /* 7-bit i2c slave address */ +#define REGMASK 0x1f +#define PROCNAME "temperature" + +/* i2c */ +static struct i2c_driver driver; +static struct i2c_client client_template; +static struct i2c_client *client = &client_template; + +/* ---------------------------------------------------------------------- */ + +static unsigned int gl_sysmon_read(int reg, int count) +{ + struct i2c_msg msgs[2]; + char write[1]={0}; + char read[2]={0,0}; + int ret; + unsigned int val; + + msgs[0].addr = msgs[1].addr = client->addr; + msgs[0].flags = msgs[1].flags = + (client->flags & (I2C_M_TEN|I2C_M_TENMASK)); + + msgs[0].len = 1; + msgs[0].buf = write; + write[0] = reg & REGMASK; + + msgs[1].flags |= I2C_M_RD; + msgs[1].len = count; + msgs[1].buf = read; + + ret = i2c_transfer(client->adapter, msgs, 2); + + if ( 2 != ret ) return ret; + if (count==1) + val = read[0] & 0xff; + else + val = ( (read[0]<<8)|(read[1] & 0xff) ) & 0xffff; + + return val; +} + +static int gl_sysmon_write(int reg, unsigned int val, int count) +{ + unsigned char buffer[5]; + + buffer[0] = reg & REGMASK; + if (count==2) { + buffer[1] = (val >> 8) & 0xff; + buffer[2] = val & 0xff; + count = 3; + } else { + buffer[1] = val & 0xff; + count = 2; + } + + if (count != i2c_master_send(client, buffer, count)) + return -1; + return 0; +} + +static int gl_sysmon_info(char *buf, char **start, off_t fpos, int length, int dummy) +{ + char * p=buf; + int i; + unsigned int regs[32]; + + for (i=0; i<32; i++) { + regs[i] = 0; + regs[i] = gl_sysmon_read(i, (i>6 && i<13) ? 2 : 1); + } + + p += sprintf(buf, "CPU temperature %d Celsius\n", (regs[4]-119)); + + return p - buf; +} + +/* ----------------------------------------------------------------------- */ + +static struct proc_dir_entry *ent; + +static int gl_sysmon_attach(struct i2c_adapter *adap) +{ + client->adapter = adap; + + i2c_attach_client(client); + gl_sysmon_write(3, 64, 1); + + ent = create_proc_entry(PROCNAME, 0, 0); + ent->get_info = gl_sysmon_info; + + printk("EISCA Fan connected\n"); + return 0; +} + +static int gl_sysmon_detach(struct i2c_client *client) +{ + gl_sysmon_write(3, 128, 1); + i2c_detach_client(client); + remove_proc_entry(PROCNAME, 0); + + printk("EISCA Fan disconnected\n"); + return 0; +} + +static int +gl_sysmon_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return -1; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver driver = { + "GL518SM", + -1, + DF_NOTIFY, + gl_sysmon_attach, + gl_sysmon_detach, + gl_sysmon_command, +}; + +static struct i2c_client client_template = { + "EISCA Cooler", + I2C_DRIVERID_GL518SM, + 0, + I2C_GL518SM, + NULL, + &driver +}; + +#ifdef MODULE +MODULE_AUTHOR("Kyosti Malkki "); +MODULE_DESCRIPTION("i2c driver for GL518SM"); + +int init_module(void) +{ + i2c_add_driver(&driver); + return 0; +} + +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif diff --git a/i2c/i2c-core.c b/i2c/i2c-core.c new file mode 100644 index 00000000..03ae5532 --- /dev/null +++ b/i2c/i2c-core.c @@ -0,0 +1,467 @@ +/* ------------------------------------------------------------------------- */ +/* i2c.c - a device driver for the iic-bus interface */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-98 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ +#define RCSID "$Id: i2c.c,v 1.7 1998/09/28 06:45:38 i2c Exp i2c $" +/* ------------------------------------------------------------------------- */ + +#include +#include +#include +#include + +#include "i2c.h" + +/* ----- global defines ---------------------------------------------------- */ + +/* exclusive access to the bus */ +/*#define SPINLOCK*/ +#ifdef SPINLOCK +#define I2C_LOCK(adap) spin_lock_irqsave(&adap->lock,adap->lockflags) +#define I2C_UNLOCK(adap) spin_unlock_irqrestore(&adap->lock,adap->lockflags) +#else +#define I2C_LOCK(adap) down(&adap->lock) +#define I2C_UNLOCK(adap) up(&adap->lock) +#endif + +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; + +/* ----- global variables -------------------------------------------------- */ + +/**** algorithm list */ +struct i2c_algorithm *algorithms[I2C_ALGO_MAX]; +int algo_count; + +/**** adapter list */ +struct i2c_adapter *adapters[I2C_ADAP_MAX]; +int adap_count; + +/**** drivers list */ +struct i2c_driver *drivers[I2C_DRIVER_MAX]; +int driver_count; + +/**** debug level */ +int i2c_debug=1; + +/* --------------------------------------------------- + * registering functions + * --------------------------------------------------- + */ + +/* ----- + * Algorithms - used to access groups of similar hw adapters or + * specific interfaces like the PCF8584 et al. + */ +int i2c_add_algorithm(struct i2c_algorithm *algo) +{ + int i; + + for (i = 0; i < I2C_ALGO_MAX; i++) + if (NULL == algorithms[i]) + break; + if (I2C_ALGO_MAX == i) { + printk(KERN_WARNING + " i2c: register_algorithm(%s) - enlarge I2C_ALGO_MAX.\n", + algo->name); + return -ENOMEM; + } + + algorithms[i] = algo; + algo_count++; + + DEB(printk("i2c: algorithm %s registered.\n",algo->name)); + return 0; +} + + +int i2c_del_algorithm(struct i2c_algorithm *algo) +{ + int i; + + for (i = 0; i < I2C_ALGO_MAX; i++) + if (algo == algorithms[i]) + break; + if (I2C_ALGO_MAX == i) { + printk(KERN_WARNING + " i2c: unregister_algorithm: [%s] not found.\n", + algo->name); + return -ENODEV; + } + algorithms[i] = NULL; + algo_count--; + + DEB(printk("i2c: algorithm unregistered: %s\n",algo->name)); + return 0; +} + + +/* ----- + * i2c_add_adapter is called from within the algorithm layer, + * when a new hw adapter registers. A new device is register to be + * available for clients. + */ +int i2c_add_adapter(struct i2c_adapter *adap) +{ + int i; + + for (i = 0; i < I2C_ADAP_MAX; i++) + if (NULL == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk(KERN_WARNING + " i2c: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n", + adap->name); + return -ENOMEM; + } + + + adapters[i] = adap; + adap_count++; + + /* init data types */ +#ifdef SPINLOCK + adap->lock = (spinlock_t)SPIN_LOCK_UNLOCKED; +#else + adap->lock = MUTEX; +#endif + + /* inform drivers of new adapters */ + for (i=0;iflags&DF_NOTIFY) + drivers[i]->attach_adapter(adap); + + DEB(printk("i2c: adapter %s registered.\n",adap->name)); + return 0; +} + +int i2c_del_adapter(struct i2c_adapter *adap) +{ + int i,j; + + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adap == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk(KERN_WARNING + " i2c: unregister_adapter adap [%s] not found.\n", + adap->name); + return -ENODEV; + } + + /* detach any active clients */ + for (j=0;jclients[j]; + if ( (client!=NULL) + /* && (client->driver->flags & DF_NOTIFY) */ ) + /* detaching devices is unconditional of the set notify + * flag, as _all_ clients that reside on the adapter + * must be deleted, as this would cause invalid states. + */ + i2c_detach_client(client); + } + /* all done, now unregister */ + adapters[i] = NULL; + adap_count--; + + DEB(printk("i2c: adapter unregistered: %s\n",adap->name)); + return 0; +} + +/* ----- + * What follows is the "upwards" interface: commands for talking to clients, + * which implement the functions to access the physical information of the + * chips. + */ + +int i2c_add_driver(struct i2c_driver *driver) +{ + int i; + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (NULL == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING + " i2c: register_driver(%s) - enlarge I2C_DRIVER_MAX.\n", + driver->name); + return -ENOMEM; + } + + drivers[i] = driver; + driver_count++; + + DEB(printk("i2c: driver %s registered.\n",driver->name)); + + /* now look for instances of driver on our adapters + */ + if ( driver->flags&DF_NOTIFY ) + for (i=0;iattach_adapter(adapters[i]); + + return 0; +} + +int i2c_del_driver(struct i2c_driver *driver) +{ + int i,j,k; + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING " i2c: unregister_driver: [%s] not found\n", + driver->name); + return -ENODEV; + } + /* Have a look at each adapter, if clients of this driver are still + * attached. If so, detach them to be able to kill the driver afterwards. + */ + DEB2(printk("i2c: unregister_driver - looking for clients.\n")); + /* removing clients does not depend on the notify flag, else + * invalid operation might (will!) result, when using stale client + * pointers. + */ + for (k=0;kname)); + for (j=0;jclients[j]; + if (client != NULL && client->driver == driver) { + DEB2(printk("i2c: detaching client %s:\n", + client->name)); + /*i2c_detach_client(client);*/ + driver->detach_client(client); + } + } + } + drivers[i] = NULL; + driver_count--; + + DEB(printk("i2c: driver unregistered: %s\n",driver->name)); + return 0; +} + + +int i2c_attach_client(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct i2c_algorithm *algo = adapter->algo; + int i; + + for (i = 0; i < I2C_CLIENT_MAX; i++) + if (NULL == adapter->clients[i]) + break; + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING + " i2c: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", + client->name); + return -ENOMEM; + } + + adapter->clients[i] = client; + adapter->client_count++; + if (algo->client_register != NULL) + algo->client_register(client); + DEB(printk("i2c: client [%s] registered to adapter [%s](pos. %d).\n", + client->name, adapter->name,i)); + return 0; +} + + +int i2c_detach_client(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct i2c_algorithm *algo = adapter->algo; + int i; + + for (i = 0; i < I2C_CLIENT_MAX; i++) + if (client == adapter->clients[i]) + break; + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING " i2c: unregister_client [%s] not found\n", + client->name); + return -ENODEV; + } + + if (algo->client_unregister != NULL) + algo->client_unregister(client); + /* client->driver->detach_client(client);*/ + adapter->clients[i] = NULL; + adapter->client_count--; + DEB(printk("i2c: client [%s] unregistered.\n",client->name)); + return 0; +} + +int i2c_init(void) +{ + /* clear algorithms */ + memset(algorithms,0,sizeof(algorithms)); + memset(adapters,0,sizeof(adapters)); + memset(drivers,0,sizeof(drivers)); + algo_count=0; + adap_count=0; + driver_count=0; + + printk("i2c module initialized.\n"); + return 0; +} + + +/* ---------------------------------------------------- + * the functional interface to the i2c busses. + * ---------------------------------------------------- + */ + +int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num) +{ + int ret; + + DEB(printk("master_xfer: %s with %d msgs.\n",adap->name,num)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,msgs,num); + I2C_UNLOCK(adap); + + return ret; +} + +int i2c_master_send(struct i2c_client *client,const char *buf ,int count) +{ + int ret; + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + msg.addr = client->addr; + msg.flags = client->flags & ( I2C_M_TEN|I2C_M_TENMASK ); + msg.len = count; + (const char *)msg.buf = buf; + + DEB(printk("master_send: writing %d bytes on %s.\n", + count,client->adapter->name)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,&msg,1); + I2C_UNLOCK(adap); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; +} + +int i2c_master_recv(struct i2c_client *client, char *buf ,int count) +{ + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + int ret; + msg.addr = client->addr; + msg.flags = client->flags & ( I2C_M_TEN|I2C_M_TENMASK ); + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = buf; + + DEB(printk("master_recv: reading %d bytes on %s.\n", + count,client->adapter->name)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,&msg,1); + I2C_UNLOCK(adap); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; +} + + +int i2c_control(struct i2c_client *client, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct i2c_adapter *adap = client->adapter; + + DEB2(printk("i2c ioctl, cmd: 0x%x, arg: %#lx\n", cmd, arg)); + switch ( cmd ) { + case I2C_RETRIES: + adap->retries = arg; + break; + case I2C_TIMEOUT: + adap->timeout = arg; + break; + default: + if (adap->algo->algo_control!=NULL) + ret = adap->algo->algo_control(adap,cmd,arg); + } + return ret; +} + +int i2c_probe(struct i2c_client *client, int low_addr, int hi_addr) +{ + int i; + struct i2c_msg msg; + struct i2c_msg *pmsg = &msg; + msg.flags=client->flags & (I2C_M_TENMASK | I2C_M_TEN ); + msg.buf = NULL; + msg.len = 0; + I2C_LOCK(client->adapter); + for (i = low_addr; i <= hi_addr; i++) { + client->addr=i; + /* TODO: implement a control statement in the algo layer + * that does address lookup only. + */ + if (1 == client->adapter-> + algo->master_xfer(client->adapter,pmsg,1)) + break; + } + I2C_UNLOCK(client->adapter); + return (i <= hi_addr) ? i : -1; +} + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus main module"); +MODULE_PARM(i2c_debug, "i"); +MODULE_PARM_DESC(i2c_debug,"debug level"); + +EXPORT_SYMBOL(i2c_add_algorithm); +EXPORT_SYMBOL(i2c_del_algorithm); +EXPORT_SYMBOL(i2c_add_adapter); +EXPORT_SYMBOL(i2c_del_adapter); +EXPORT_SYMBOL(i2c_add_driver); +EXPORT_SYMBOL(i2c_del_driver); +EXPORT_SYMBOL(i2c_attach_client); +EXPORT_SYMBOL(i2c_detach_client); + +EXPORT_SYMBOL(i2c_master_send); +EXPORT_SYMBOL(i2c_master_recv); +EXPORT_SYMBOL(i2c_control); +EXPORT_SYMBOL(i2c_transfer); + + +int init_module(void) +{ + return i2c_init(); +} + +void cleanup_module(void) +{ +} +#endif diff --git a/i2c/i2c-dev.c b/i2c/i2c-dev.c new file mode 100644 index 00000000..44d76d6d --- /dev/null +++ b/i2c/i2c-dev.c @@ -0,0 +1,364 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-dev.c - i2c-bus driver, char device interface */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ +#define RCSID "$Id: i2c-dev.c,v 1.6 1998/09/28 06:45:38 i2c Exp i2c $" +/* ------------------------------------------------------------------------- */ + +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= 0x020100 +# include +#else +# include +#endif + +#include "i2c.h" + +/* ----- global defines ---------------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEBE(x) x /* error messages ~ */ +#define DEBI(x) /* ioctl and its arguments */ + +/* ----- global variables -------------------------------------------------- */ + +/* save the registered char devices. + * there is one device per i2c adapter. + */ +#define I2C_DEV_MAX I2C_ADAP_MAX +struct i2c_client *devs[I2C_DEV_MAX]; +int dev_count; + +struct i2c_driver driver; +struct i2c_client dev_template = { + "I2C char device", /* name */ + -1, /* id */ + 0, /* flags */ + 0, /* address */ + NULL, /* adapter */ + &driver, /* driver */ +}; + + +/* ----- local functions --------------------------------------------------- */ + +#if LINUX_VERSION_CODE >= 0x020100 +long long i2c_lseek(struct file * file, long long offset, int origin) +#else +int i2c_lseek (struct inode * inode, struct file *file, off_t offset, int origin) +#endif +{ + return -ESPIPE; +} + + +int i2c_open(struct inode * inode, struct file * file) +{ + unsigned int minor = MINOR(inode->i_rdev); + + if (minor >= dev_count ) { + DEBE(printk("i2c: minor exceeded ports\n")); + return -ENODEV; + } + file->private_data = devs[minor]; + MOD_INC_USE_COUNT; + i2c_attach_client(devs[minor]); /* tell adapter it is in use. */ + + DEB(printk("i2c_open: i2c%d\n",minor)); + return 0; +} + +#if LINUX_VERSION_CODE >= 0x020100 +int i2c_release (struct inode * inode, struct file * file) +#else +void i2c_release (struct inode * inode, struct file * file) +#endif +{ + unsigned int minor = MINOR(inode->i_rdev); + + file->private_data=NULL; + DEB(printk("i2c_close: i2c%d\n",minor)); + i2c_detach_client(devs[minor]); + MOD_DEC_USE_COUNT; +#if LINUX_VERSION_CODE >= 0x020100 + return 0; +#endif +} + +#if LINUX_VERSION_CODE >= 0x020140 /* file operations changed again...*/ +ssize_t i2c_write(struct file * file, + const char * buf, size_t count, loff_t *ppos) +#else +# if LINUX_VERSION_CODE >= 0x020100 +long i2c_write(struct inode * inode, struct file * file, + const char * buf, unsigned long count) +# else +int i2c_write (struct inode *inode, struct file *file, + const char *buf, int count) +# endif +#endif +{ +#if LINUX_VERSION_CODE >= 0x020140 + struct inode *inode = file->f_dentry->d_inode; +#endif + int ret; + char *tmp; + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client = + (struct i2c_client *)file->private_data; + + if (minor>=dev_count){ + DEBE(printk("i2c_write: minor %d invalid\n",minor)); + return -EINVAL; + } + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; +#if LINUX_VERSION_CODE >= 0x020100 + copy_from_user(tmp,buf,count); +#else + memcpy_fromfs(tmp,buf,count); +#endif + + DEB(printk("i2c_write: i2c%d writing %d bytes.\n",minor,count)); + + ret = i2c_master_send(client,tmp,count); + kfree(tmp); + return ret; +} + +#if LINUX_VERSION_CODE >= 0x020140 /* file operations changed again...*/ +ssize_t i2c_read(struct file * file,char * buf, size_t count, loff_t *ppos) +#else +#if LINUX_VERSION_CODE >= 0x020100 +long i2c_read(struct inode * inode, struct file * file, + char * buf, unsigned long count) +#else +int i2c_read (struct inode *inode, struct file *file, char *buf, int count) +#endif +#endif +{ +#if LINUX_VERSION_CODE >= 0x020140 + struct inode *inode = file->f_dentry->d_inode; +#endif + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client = + (struct i2c_client *)file->private_data; + char *tmp; + int ret; + + DEB(printk("i2c_read: i2c%d reading %d bytes from %d.\n",minor,count,client->addr)); + if (minor>=dev_count) { + DEBE(printk("i2c_write: minor %d invalid\n",minor)); + return -EINVAL; + } + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + ret = i2c_master_recv(client,tmp,count); +#if LINUX_VERSION_CODE >= 0x020100 + copy_to_user(buf,tmp,count); +#else + memcpy_tofs(buf,tmp,count); +#endif + + return ret; +} + + +int i2c_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client = + (struct i2c_client *)file->private_data; + int ret = 0; + + DEBI(printk("i2c ioctl, cmd: 0x%x, arg: %#lx\n", cmd, arg)); + switch ( cmd ) { + case I2C_SLAVE: + client->addr = arg; + break; + case I2C_TENBIT: + client->flags &= !I2C_M_TENMASK; + if (arg<0) + return 0; + switch(arg) { + case 0: + client->flags|=I2C_M_TEN0; + break; + case 1: + client->flags|=I2C_M_TEN1; + break; + case 2: + client->flags|=I2C_M_TEN2; + break; + case 3: + client->flags|=I2C_M_TEN3; + break; + default: + printk("i2c-dev(%d): illegal arg to I2C_TEN: %ld\n" + ,minor,arg); + }; + break; +#if 0 + case I2C_WRITE_SIZE: + if ( arg >= I2C_BUFFER_SIZE ) { + printk("i2c%d: write size too big (%ld)",minor,arg); + return -E2BIG; + } + data->writelength = arg; + if ( arg > 0 ) + i2c_table[minor].flags|= P_REG; + else + i2c_table[minor].flags&=!P_REG; + break; + case I2C_WRITE_BUF: +#if LINUX_VERSION_CODE >= 0x020100 + copy_from_user(data->buf,(char*)arg,data->writelength); +#else + memcpy_fromfs(data->buf,(char*)arg,data->writelength); +#endif + break; +#endif + default: + ret = i2c_control(client,cmd,arg); + } + DEB(printk("i2c(dev): handled ioctl (%d,%ld)\n",cmd,arg)); + return ret; +} + + +/* ----- module functions --------------------------------------------- */ +struct file_operations i2c_fops = { + i2c_lseek, + i2c_read, + i2c_write, + NULL, /* i2c_readdir */ + NULL, /* i2c_select */ + i2c_ioctl, + NULL, /* i2c_mmap */ + i2c_open, +#if LINUX_VERSION_CODE >= 0x020178 + NULL, /* i2c_flush */ +#endif + i2c_release, +}; + +int attach_adapter(struct i2c_adapter *adap) +{ + int i; + struct i2c_client *client; + + for (i = 0; i < I2C_DEV_MAX; i++) + if (NULL == devs[i]) + break; + if (I2C_DEV_MAX == i) + return -ENOMEM; + + client = (struct i2c_client *)kmalloc(sizeof(struct i2c_client), + GFP_KERNEL); + if (client==NULL) + return -ENOMEM; + memcpy(client,&dev_template,sizeof(struct i2c_client)); + client->adapter = adap; +/* for combined r/w: + client->data = kmalloc(sizeof(char)*I2C_BUFFER_SIZE,GFP_KERNEL); +*/ + devs[i] = client; + dev_count++; + + DEB(printk("i2c(char): registered '%s' as minor %d\n",adap->name,i)); + return 0; +} + +int detach_adapter(struct i2c_client *client) +{ + struct i2c_adapter *adap = client->adapter; + int i; + for (i = 0; i < I2C_DEV_MAX; i++) + if (devs[i]->adapter == adap) + break; + if (I2C_DEV_MAX == i) + { + printk(KERN_WARNING "i2c(char): detach adapter %s not found\n", + adap->name); + return -ENODEV; + } + if (devs[i]->data) + kfree(devs[i]->data); + kfree(devs[i]); + devs[i] = NULL; + dev_count--; + + DEB(printk("i2c(char): adapter unregistered: %s\n",adap->name)); + return 0; +} + +int command(struct i2c_client *client,unsigned int cmd, void *arg) +{ + return -1; +} + + + +struct i2c_driver driver = { + "i2c character device", + 2, /* id */ + DF_NOTIFY, /* flags */ + attach_adapter, + detach_adapter, + command, +}; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus character device interface"); + +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if (register_chrdev(I2C_MAJOR,"i2c",&i2c_fops)) { + printk("i2c: unable to get major %d for i2c bus\n",I2C_MAJOR); + return -EIO; + } + + memset(&devs,0,sizeof(devs)); + dev_count = 0; + + i2c_add_driver(&driver); + printk("i2c char device interface initialized.\n"); + return 0; +} + + +void cleanup_module(void) +{ + i2c_del_driver(&driver); + unregister_chrdev(I2C_MAJOR,"i2c"); +} + +#endif diff --git a/i2c/i2c.h b/i2c/i2c.h new file mode 100644 index 00000000..bb8a1b5a --- /dev/null +++ b/i2c/i2c.h @@ -0,0 +1,371 @@ +/* ------------------------------------------------------------------------- */ +/* */ +/* i2c.h - definitions for the \iic-bus interface */ +/* */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ +#ifndef _I2C_H +#define _I2C_H + +#ifdef __KERNEL__ + +/* define spinlock to use spinlocks for sync., else use semaphores*/ +/*#define SPINLOCK*/ + +#ifdef SPINLOCK +#include /* for spinlock_t */ +#else +#include +#endif +/* --- General options ------------------------------------------------ */ + +#define I2C_ALGO_MAX 4 /* control memory consumption */ +#define I2C_ADAP_MAX 16 +#define I2C_DRIVER_MAX 16 +#define I2C_CLIENT_MAX 32 + +struct i2c_msg; +struct i2c_algorithm; +struct i2c_adapter; +struct i2c_client; +struct i2c_driver; + + +/* + * The master routines are the ones normally used to transmit data to devices + * on a bus (or read from them). Apart from two basic transfer functions to transmit + * one message at a time, a more complex version can be used to transmit an arbitrary + * number of messages without interruption. + */ +extern int i2c_master_send(struct i2c_client *,const char* ,int); +extern int i2c_master_recv(struct i2c_client *,char* ,int); + +/* Transfer num messages. + */ +extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],int num); + + +/* + * Some adapter types (i.e. PCF 8584 based ones) may support slave behaviuor. + * This is not tested/implemented yet and will change in the future. + */ +extern int i2c_slave_send(struct i2c_client *,char*,int); +extern int i2c_slave_recv(struct i2c_client *,char*,int); + + + +/* + * I2C Message - could be used in the current interface to + */ +struct i2c_msg { + unsigned char addr; /* slave address */ + unsigned short flags; +#define I2C_M_TEN 0x10 /* we have a ten bit chip address */ +#define I2C_M_TEN0 0x10 /* herein lie the first 2 bits */ +#define I2C_M_TEN1 0x12 +#define I2C_M_TEN2 0x14 +#define I2C_M_TEN3 0x16 +#define I2C_M_TENMASK 0x06 +#define I2C_M_RD 0x01 + short len; /* msg length */ + char *buf; /* pointer to msg data */ +}; + +/* + * A driver is capable of handling one or more physical devices present on + * I2C adapters. This information is used to inform the driver of adapter + * events. + */ + +struct i2c_driver { + char name[32]; + int id; + unsigned int flags; /* div., see below */ + + /* notifies the driver that a new bus has appeared. This routine + * can be used by the driver to test if the bus meets its conditions + * & seek for the presence of the chip(s) it supports. If found, it + * registers the client(s) that are on the bus to the i2c admin. via + * i2c_attach_client + */ + int (*attach_adapter)(struct i2c_adapter *); + + /* tells the driver that a client is about to be deleted & gives it + * the chance to remove its private data. Also, if the client struct + * has been dynamically allocated by the driver in the function above, + * it must be freed here. + */ + int (*detach_client)(struct i2c_client *); + + /* a ioctl like command that can be used to perform specific functions + * with the device. + */ + int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); + + /* These two are mainly used for bookkeeping & dynamic unloading of + * kernel modules. inc_use tells the driver that a client is being + * used by another module & that it should increase its ref. counter. + * dec_use is the inverse operation. + * NB: Make sure you have no circular dependencies, or else you get a + * deadlock when trying to unload the modules. + */ + void (*inc_use)(struct i2c_client *client); + void (*dec_use)(struct i2c_client *client); +}; + +/* + * i2c_client identifies a single device (i.e. chip) that is connected to an + * i2c bus. The behaviour is defined by the routines of the driver. This + * function is mainly used for lookup & other admin. functions. + */ +struct i2c_client { + char name[32]; + int id; + unsigned int flags; /* div., see below */ + unsigned char addr; /* chip address - NOTE: 7bit */ + /* addresses are stored in the */ + /* _LOWER_ 7 bits of this char */ + /* 10 bit addresses use the full*/ + /* 8 bits & the flags like in */ + /* i2c_msg */ + struct i2c_adapter *adapter; /* the adapter we sit on */ + struct i2c_driver *driver; /* and our access routines */ + void *data; /* for the clients */ +}; + + +/* + * The following structs are for those who like to implement new bus drivers: + * i2c_algorithm is the interface to a class of hardware solutions which can + * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 + * to name two of the most common. + */ +struct i2c_algorithm { + char name[32]; /* textual description */ + unsigned int id; + /* + int (*master_send)(struct i2c_client *,const char*,int); + int (*master_recv)(struct i2c_client *,char*,int); + int (*master_comb)(struct i2c_client *,char*,const char*,int,int,int); + */ + int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg msgs[], int num); + + /* --- these optional/future use for some adapter types.*/ + int (*slave_send)(struct i2c_adapter *,char*,int); + int (*slave_recv)(struct i2c_adapter *,char*,int); + + /* --- ioctl like call to set div. parameters. */ + int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long); + + /* --- administration stuff. */ + int (*client_register)(struct i2c_client *); + int (*client_unregister)(struct i2c_client *); +}; + + +/* + * i2c_adapter is the structure used to identify a physical i2c bus along + * with the access algorithms necessary to access it. + */ +struct i2c_adapter { + char name[32]; /* some useful name to identify the adapter */ + unsigned int id;/* == is algo->id | hwdep.struct->id, */ + /* for registered values see below */ + struct i2c_algorithm *algo;/* the algorithm to access the bus */ + + void *data; /* private data for the adapter */ + /* some data fields that are used by all types */ + /* these data fields are readonly to the public */ + /* and can be set via the i2c_ioctl call */ + + /* data fields that are valid for all devices */ +#ifdef SPINLOCK + spinlock_t lock;/* used to access the adapter exclusively */ + unsigned long lockflags; +#else + struct semaphore lock; +#endif + unsigned int flags;/* flags specifying div. data */ + + struct i2c_client *clients[I2C_CLIENT_MAX]; + int client_count; + + int timeout; + int retries; +}; + + +/*flags for the driver struct: + */ +#define DF_NOTIFY 0x01 /* notify on bus (de/a)ttaches */ + + +#if 0 /* deprecate! */ +/*flags for the client struct: + */ +#define CF_TEN 0x100000 /* we have a ten bit chip address */ +#define CF_TEN0 0x100000 /* herein lie the first 2 bits */ +#define CF_TEN1 0x110000 +#define CF_TEN2 0x120000 +#define CF_TEN3 0x130000 +#define TENMASK 0x130000 +#endif + +/* ----- functions exported by i2c.o */ + +/* administration... + */ +extern int i2c_add_algorithm(struct i2c_algorithm *); +extern int i2c_del_algorithm(struct i2c_algorithm *); + +extern int i2c_add_adapter(struct i2c_adapter *); +extern int i2c_del_adapter(struct i2c_adapter *); + +extern int i2c_add_driver(struct i2c_driver *); +extern int i2c_del_driver(struct i2c_driver *); + +extern int i2c_attach_client(struct i2c_client *); +extern int i2c_detach_client(struct i2c_client *); + + +/* + * A utility function used in the attach-phase of drivers. Returns at the first address + * that acks in the given range. + */ +extern int i2c_probe(struct i2c_client *client, int low_addr, int hi_addr); + +/* An ioctl like call to set div. parameters of the adapter. + */ +extern int i2c_control(struct i2c_client *,unsigned int, unsigned long); + + +#endif /* __KERNEL__ */ + + +/* ----- commands for the ioctl like i2c_command call: + * note that additional calls are defined in the algorithm and hw + * dependent layers - these can be listed here, or see the + * corresponding header files. + */ + /* -> bit-adapter specific ioctls */ +#define I2C_RETRIES 0x0701 /* number times a device adress should */ + /* be polled when not acknowledging */ +#define I2C_TIMEOUT 0x0702 /* set timeout - call with int */ + + +/* this is for i2c-dev.c */ +#define I2C_SLAVE 0x0703 /* Change slave address */ + /* Attn.: Slave address is 7 bits long, */ + /* these are to be passed as the */ + /* lowest 7 bits in the arg. */ + /* for 10-bit addresses pass lower 8bits*/ +#define I2C_TENBIT 0x0704 /* with 0-3 as arg to this call */ + /* a value <0 resets to 7 bits */ +/* ... algo-bit.c recognizes */ +#define I2C_UDELAY 0x0705 /* set delay in microsecs between each */ + /* written byte (except address) */ +#define I2C_MDELAY 0x0706 /* millisec delay between written bytes */ + +#if 0 +#define I2C_ADDR 0x0707 /* Change adapter's \iic address */ + /* ...not supported by all adap's */ + +#define I2C_RESET 0x07fd /* reset adapter */ +#define I2C_CLEAR 0x07fe /* when lost, use to clear stale info */ +#define I2C_V_SLOW 0x07ff /* set jiffies delay call with int */ + +#define I2C_INTR 0x0708 /* Pass interrupt number - 2be impl. */ + +#endif + +/* + * ---- Driver types ----------------------------------------------------- + */ + +#define I2C_DRIVERID_MSP3400 1 +#define I2C_DRIVERID_TUNER 2 +#define I2C_DRIVERID_VIDEOTEXT 3 +#define I2C_DRIVERID_GL518SM 4 + +/* + * ---- Adapter types ---------------------------------------------------- + * + * First, we distinguish between several algorithms to access the hardware + * interface types, as a PCF 8584 needs other care than a bit adapter. + */ + +#define ALGO_NONE 0x00000 +#define ALGO_BIT 0x10000 /* bit style adapters */ +#define ALGO_PCF 0x20000 /* PCF 8584 style adapters */ + +#define ALGO_MASK 0xf0000 /* Mask for algorithms */ +#define ALGO_SHIFT 0x10 /* right shift to get index values */ + +#define I2C_HW_ADAPS 0x10000 /* number of different hw implements per*/ + /* algorithm layer module */ +#define I2C_HW_MASK 0xffff /* space for indiv. hw implmentations */ + + +/* hw specific modules that are defined per algorithm layer + */ + +/* --- Bit algorithm adapters */ +#define HW_B_LP 0x00 /* Parallel port Philips style adapter */ +#define HW_B_LPC 0x01 /* Parallel port, over control reg. */ +#define HW_B_SER 0x02 /* Serial line interface */ +#define HW_B_ELV 0x03 /* ELV Card */ +#define HW_B_VELLE 0x04 /* Vellemann K8000 */ +#define HW_B_BT848 0x05 /* BT848 video boards */ +#define HW_B_WNV 0x06 /* Winnov Videums */ +#define HW_B_MB 0x07 /* Via vt82c586b */ + +/* --- PCF 8584 based algorithms */ +#define HW_P_LP 0x00 /* Parallel port interface */ +#define HW_P_ISA 0x01 /* generic ISA Bus inteface card */ +#define HW_P_ELEK 0x02 /* Elektor ISA Bus inteface card */ + + + + +/* ----- I2C-DEV: char device interface stuff ------------------------- */ + +#define I2C_MAJOR 89 /* Device major number */ + + +# if LINUX_VERSION_CODE < 0x020100 +/* Hack to make this thing compile under 2.0.xx kernels + */ + +# ifdef MODULE +# define MODULE_AUTHOR(noone) +# define MODULE_DESCRIPTION(none) +# define MODULE_PARM(no,param) +# define MODULE_PARM_DESC(no,description) +# define EXPORT_SYMBOL(noexport) +# define EXPORT_NO_SYMBOLS +# endif + +# ifndef NULL +# define NULL ( (void *) 0 ) +# endif +#endif + +# ifndef ENODEV +# include +# endif +#endif diff --git a/i2c/old-code/algo-pcf.c b/i2c/old-code/algo-pcf.c new file mode 100644 index 00000000..a8e36236 --- /dev/null +++ b/i2c/old-code/algo-pcf.c @@ -0,0 +1,291 @@ +/* ------------------------------------------------------------------------- */ +/* adap-bit.c i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ +static char alg_rcsid[] = "$Id: algo-pcf.c,v 1.1 1998/09/05 18:20:09 i2c Exp $"; + +#include +#include +#include +#include +#include + + +#if LINUX_VERSION_CODE >= 0x020100 + +# include +#else +# include +#endif + + +#include +#include +#include + +#include "i2c.h" +#include "algo-pcf.h" + +/* ----- global defines ---------------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) /* error messages */ +#define DEBI(x) /* ioctl and its arguments */ +#define DEBACK(x) x /* ack failed message */ +#define DEBSTAT(x) /* print several statistical values */ + +#define DEBPROTO(x) /* debug the protocol by showing transferred bytes*/ + + +/* ----- global variables --------------------------------------------- */ + +/* module parameters: + */ +static int test=0; /* see if the line-setting functions work */ +static int scan=0; /* have a look at what's hanging 'round */ + +/* + * This array contains the hw-specific functions for + * each port (hardware) type. + */ +static struct pcf_adapter *pcf_adaps[PCF_ADAP_MAX]; +static int adap_count; +static struct i2c_adapter *i2c_adaps[PCF_ADAP_MAX]; + +/* --- setting states on the bus with the right timing: --------------- */ + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct pcf_adapter *adap) +{ +} + +/* ----- Utility functions + */ + + +/* send a message to a client. + */ +static int pcf_send(struct i2c_client *client,const char *buf, int count) +{ + struct i2c_adapter *adapter=client->adapter; + struct pcf_adapter *adap=(struct pcf_adapter*)adapter->data; + int ret,i; + + DEB2(printk(" i2c_write: wrote %d bytes.\n",wrcount)); + return wrcount; +} + + +static int pcf_recv(struct i2c_client *client,char *buf,int count) +{ + struct i2c_adapter *adapter = client->adapter; + struct pcf_adapter *adap = (struct pcf_adapter*)adapter->data; + unsigned int flags = client->flags; + char addr; + int ret=0,i,rdcount; + + DEB(printk("i2c(bit): i2c_read: %d byte(s) read.\n", rdcount )); + return rdcount; +} + + +/* + * alpha version of combined transmit + */ +static int pcf_comb(struct i2c_client *client, char *readbuf,const char *writebuf, + int nread, int nwrite, int dir) +{ + /* struct i2c_adapter *adapter = client->adapter; + struct pcf_adapter *adap = (struct pcf_adapter*)adapter->data; + unsigned int flags = client->flags; + char addr; + int ret=0,i,rdcount=0,wrcount=0; + + + DEB(printk("i2c(bit): i2c_read: %d byte(s) read.\n", rdcount )); + return wrcount+rdcount; + */ + return 0; +} + + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static int client_register(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct pcf_adapter *adap = (struct pcf_adapter*)adapter->data; + + if (adap->client_register != NULL) + return adap->client_register(client); + return 0; +} + +int client_unregister(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct pcf_adapter *adap = (struct pcf_adapter*)adapter->data; + + if (adap->client_unregister != NULL) + return adap->client_unregister(client); + return 0; +} + +/* -----exported algorithm data: ------------------------------------- */ + +struct i2c_algorithm pcf_algo = { + "PCF 8584 algorithm", + ALGO_BIT, + pcf_send, /* master_xmit */ + pcf_recv, /* master_recv */ + pcf_comb, /* master_comb */ + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + client_register, + client_unregister, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pcf_add_bus(struct pcf_adapter *adap) +{ + int i,ack; + struct i2c_adapter *i2c_adap; + + for (i = 0; i < PCF_ADAP_MAX; i++) + if (NULL == pcf_adaps[i]) + break; + if (PCF_ADAP_MAX == i) + return -ENOMEM; + + if (test) { + int ret = test_bus(adap); + if (ret<0) + return -ENODEV; + } + i2c_adap = kmalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (i2c_adap == NULL) + return -ENOMEM; + + pcf_adaps[i] = adap; + adap_count++; + DEB(printk("i2c(bit): algorithm %s registered.\n",adap->name)); + + /* register new adapter to i2c module... */ + + memset(i2c_adap,0,sizeof(struct i2c_adapter)); + strcpy(i2c_adap->name,adap->name); + i2c_adap->id = pcf_algo.id | adap->id; + i2c_adap->algo = &pcf_algo; + i2c_adap->data = adap; + i2c_adap->timeout = 100; /* default values, should */ + i2c_adap->retries = 3; /* be replaced by defines */ + i2c_adaps[i] = i2c_adap; + i2c_add_adapter(i2c_adap); + + /* scan bus */ + if (scan) { +#if 0 + printk(KERN_INFO "i2c(bit): scanning bus %s.\n", adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_start(adap); + ack = i2c_outb(adap,i); + i2c_stop(adap); + if (ack>0) { + printk(KERN_INFO + "i2c(bit): found chip at addr=0x%2x\n",i>>1); + } + } +#endif + } + return 0; +} + + +int i2c_pcf_del_bus(struct pcf_adapter *adap) +{ + int i; + + for (i = 0; i < PCF_ADAP_MAX; i++) + if ( adap == pcf_adaps[i]) + break; + if ( PCF_ADAP_MAX == i) { + printk(KERN_WARNING "i2c(bit): could not unregister bus: %s\n", + adap->name); + return -ENODEV; + } + + pcf_adaps[i] = NULL; + i2c_del_adapter(i2c_adaps[i]); + kfree(i2c_adaps[i]); + i2c_adaps[i] = NULL; + adap_count--; + DEB(printk("i2c(bit): adapter unregistered: %s\n",adap->name)); + + return 0; +} + +int algo_pcf_init (void) +{ + int i; + + for (i=0;i +#include +#include +#include +#include +#include + + +#include "i2c.h" +#include "algo-bit.h" + + +#define BIT_LP_MAX 3 /* max. number of addresses to probe */ + +struct bit_lp_data { + unsigned int base; /* base address */ +}; + +static struct bit_lp_data bit_lp_adaps[BIT_LP_MAX] = { + {0,}, + {0,}, + {0,}, + }; +static int adap_count=0; + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + +/* ----- printer port defines ------------------------------------------*/ + /* Pin Port Inverted name */ +#define I2C_ON 0x20 /* 12 status N paper */ + /* ... only for phil. not used */ +#define I2C_SDA 0x80 /* 9 data N data7 */ +#define I2C_SCL 0x08 /* 17 ctrl N dsel */ + +#define I2C_SDAIN 0x80 /* 11 stat Y busy */ +#define I2C_SCLIN 0x08 /* 15 stat Y enable */ + +#define I2C_DMASK 0x7f +#define I2C_CMASK 0xf7 + +/* --- Convenience defines for the parallel port: */ +#define BASE bit_lp_adaps[minor].base +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + +/* ----- local functions ---------------------------------------------- */ + +static void bit_lp_setscl(int minor, int state) +{ + if (state) { + outb(I2C_SCL, CTRL); + } else { + outb(I2C_CMASK, CTRL); + } +} + +static void bit_lp_setsda(int minor, int state) +{ + if (state) { + outb(I2C_DMASK , DATA); + } else { + outb(I2C_SDA , DATA); + } +} + +static int bit_lp_getscl(int minor) +{ + return ( 0 != ( (inb(STAT)) & I2C_SCLIN ) ); +} + +static int bit_lp_getsda(int minor) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +static int bit_lp_init(int minor) +{ + if (check_region(bit_lp_adaps[minor].base, + (bit_lp_adaps[minor].base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + request_region(bit_lp_adaps[minor].base, + (bit_lp_adaps[minor].base == 0x3bc)? 3 : 8, + "i2c (parallel port adapter)"); + bit_lp_setsda(minor,1); + bit_lp_setscl(minor,1); + } + printk("i2c%d: scl: %d sda %d \n",minor,bit_lp_getscl(minor),bit_lp_getsda(minor)); + return 0; +} + +static void bit_lp_exit(int minor) +{ + release_region( bit_lp_adaps[minor].base , + (bit_lp_adaps[minor].base == 0x3bc)? 3 : 8 ); + release_region( bit_lp_adaps[minor].base + 0x0400, 8 ); +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +struct bit_adapter bit_lp_ops = { + "Philips Parallel port adapter", + bit_lp_setscl, + bit_lp_setsda, + bit_lp_getscl, + bit_lp_getsda, + bit_lp_init, + bit_lp_exit +}; + +#ifdef MODULE +static int base[BIT_LP_MAX+1] = { [0 ... BIT_LP_MAX] = 0 }; +MODULE_PARM(base, "1-" __MODULE_STRING(BIT_LP_MAX) "i"); + +/* +EXPORT_SYMBOL(i2c_bit_add_bus); +EXPORT_SYMBOL(i2c_bit_del_bus); +*/ +int init_module(void) +{ + /* Work out how many ports we have, then get parport_share to parse + the irq values. */ + unsigned int i; + for (i = 0; i < BIT_LP_MAX && base[i]; i++) { + bit_lp_adaps[adap_count].base = base[i]; + if (bit_lp_init(adap_count)==0) { +/* i2c_bit_add_bus(struct bit_adapter *algo);*/ +/* bit_add_adapter();*/ + adap_count++; + } + } + if (i==0) { /* no values specified -> probe */ + } + +/* count += probe_one_port(0x3bc, irq[0], dma[0]); + count += probe_one_port(0x378, irq[0], dma[0]); + count += probe_one_port(0x278, irq[0], dma[0]); +*/ + printk("bit_lp: found %d devices.\n",adap_count); + + if (adap_count==0) + return -ENODEV; + else + return 0; +} + +void cleanup_module(void) +{ + int i; + for (i=0;i +#include +#include +#include +#include + +#include "i2c.h" +#include "i2c-priv.h" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + +#define DEBHW(x) x /* lowest level hardware debug -- log changes of*/ + /* the bit lines... */ + +#define basePort(port) (i2c_table[minor].base+(port)) + +/* ----- local functions ---------------------------------------------- */ + +#if (PCFADAPS) > 1 +# define Local static +#else +# define Local inline +#endif + + + +Local int pcf_read (int minor, int adr) +{ + int ret; + udelay(10); /* Give the card some time */ + ret = inb(basePort( (adr!=0) ));/* force max. increment of 1 */ + return ret; +} + + +Local void pcf_write (int minor, int adr,char data) +{ + udelay(10); /* Give the card some time */ + outb(data,basePort( (adr!=0) )); +} + + +Local int pcf_init (int minor) +{ + if (check_region(i2c_table[minor].base, 4) < 0 ) { + return -ENODEV; + } else { + request_region(i2c_table[minor].base, 4, "i2c (Elektor)"); + } + return 0; +} + + +Local void pcf_exit (int minor) +{ + release_region( i2c_table[minor].base, 4 ); +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +#if (PCFADAPS) > 1 +struct i2c_pcf_ops pcf_lp_ops = { + pcf_read, + pcf_write, + pcf_init, + pcf_exit +}; +#endif diff --git a/i2c/old-code/pcf-isa.c b/i2c/old-code/pcf-isa.c new file mode 100644 index 00000000..c1292541 --- /dev/null +++ b/i2c/old-code/pcf-isa.c @@ -0,0 +1,110 @@ +/* ------------------------------------------------------------------------- */ +/* pcf-isa.c i2c-hw access for PCF 8584 on ISA boards */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-96 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ +static char rcsid[] = "$Id: pcf-isa.c,v 1.1 1998/09/05 18:20:09 i2c Exp $"; +/* + * $Log: pcf-isa.c,v $ + * Revision 1.1 1998/09/05 18:20:09 i2c + * Initial revision + * + * Revision 1.3 1998/01/20 10:01:29 i2c + * *** empty log message *** + * + * Revision 1.2 1996/11/20 20:20:46 i2c + * first version for ISA bus. + * + * Revision 1.1 1996/11/17 11:00:03 i2c + * Initial revision + * + * + */ + +#include +#include +#include +#include +#include + +#include "i2c.h" +#include "i2c-priv.h" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + +/* ----- local functions --------------------------------------------------- */ + +#if (PCFADAPS) > 1 +# define Local static +#else +# define Local inline +#endif + + +Local int pcf_read (int minor, int adr) +{ + if (adr) + return inb(BASE + 1); /* read status port */ + else + return inb(BASE); /* read data port */ +} + + +Local void pcf_write (int minor, int adr,char data) +{ + if (adr) + outb(data, BASE + 1); + else + outb(data, BASE); +} + + +Local int pcf_init (int minor) +{ + if (check_region(i2c_table[minor].base, 2) < 0 ) { + DEBE(printk("i2c_init: Port %#x already in use.\n", + i2c_table[minor].base)); + return -ENODEV; + } else { + request_region(i2c_table[minor].base, 2, + "i2c (ISA bus)"); + } + return 0; +} + + +Local void pcf_exit (int minor) +{ + release_region( i2c_table[minor].base , 2 ); +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +#if (PCFADAPS) > 1 +struct i2c_pcf_ops pcf_isa_ops = { + pcf_read, + pcf_write, + pcf_init, + pcf_exit +}; +#endif \ No newline at end of file diff --git a/i2c/old-code/pcf-lp.c b/i2c/old-code/pcf-lp.c new file mode 100644 index 00000000..cbe80875 --- /dev/null +++ b/i2c/old-code/pcf-lp.c @@ -0,0 +1,228 @@ +/* ------------------------------------------------------------------------- */ +/* pcf-lp.c i2c-hw access for PCF 8584 on bidir. parallel ports */ +/* ------------------------------------------------------------------------- */ +/* +-----.-.---....------..-.-.---.... Oh bugger! Hope it works this time...... + + Copyright (C) 1995-97 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ +static char rcsid[] = "$Id: pcf-lp.c,v 1.1 1998/09/05 18:20:09 i2c Exp $"; +/* + * $Log: pcf-lp.c,v $ + * Revision 1.1 1998/09/05 18:20:09 i2c + * Initial revision + * + * Revision 1.4 1998/01/20 10:01:29 i2c + * *** empty log message *** + * + * Revision 1.3 1997/06/15 14:21:37 i2c + * removed debugging messages + * + * Revision 1.2 1997/06/03 06:00:10 i2c + * first version that works + * + * Revision 1.2 1996/11/20 20:20:46 i2c + * first version for ISA bus. + * + * Revision 1.1 1996/11/17 11:00:03 i2c + * Initial revision + * + * + */ + +#include +#include +#include +#include +#include + +#include "i2c.h" +#include "i2c-priv.h" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + +#define DEBHW(x) x /* lowest level hardware debug -- log changes of*/ + /* the bit lines... */ + +#define lpPort(port) (i2c_table[minor].base+(port)) + + +/* ----- local variables ---------------------------------------------- */ + +static unsigned char ctrl; /* remember line states */ + + /* (re)set line states */ +#define Strobe(arg) if (!arg) ctrl|= 0x01; else ctrl &=!0x01;\ + outb_p(ctrl,lpPort(2)) /* Strobe -- pin 1 */ +#define AutoLF(arg) if (!arg) ctrl|= 0x02; else ctrl &=!0x02;\ + outb_p(ctrl,lpPort(2)) /* AutoLF -- pin 14 */ +#define Init(arg) if ( arg) ctrl|= 0x04; else ctrl &=!0x04;\ + outb(ctrl,lpPort(2)) /* Init -- pin 16 */ +#define Select(arg) if (!arg) ctrl|= 0x08; else ctrl &=!0x08;\ + outb_p(ctrl,lpPort(2)) /* Sel. in - pin 17 */ + /* 0x10 -- Int Enable */ +#define ReadBit(arg) if(arg) ctrl|= 0x20; else ctrl &=!0x20;\ + outb_p(ctrl,lpPort(2)) /* direction bit */ + +#define inStr() (! (0x01 & inb(lpPort(2)) ) ) /* Strobe -- pin 1 */ +#define inALF() (! (0x02 & inb(lpPort(2)) ) ) /* AutoLF -- pin 14 */ +#define inIni() ( (0x04 & inb(lpPort(2)) ) ) /* Init -- pin 16 */ +#define inSel() (! (0x08 & inb(lpPort(2)) ) ) /* Sel. in - pin 17 */ + /* 0x10 -- Int Enable */ +/* +#define Argl() printk("(wr-%d cs-%d a0-%d (%#2x, %#2x))\n", \ + inStr(),inALF(),inIni(),inb(lpPort(0)),inb(lpPort(1)) ) +*/ +#define Argl() /**/ + +#define wr(arg) Strobe(arg); Argl() /* write line */ +#define cs(arg) AutoLF(arg); Argl() /* chip select */ +#define a0(arg) Init (arg); Argl() /* address line */ + +/* ----- local functions ---------------------------------------------- */ +/* +#if (PCFADAPS) > 1 +# define Local static +#else +# define Local inline +#endif +*/ +#define Local /**/ + +/* --------- okay, I borrowed it. Stays here for debugging. + * check the epp status. After a EPP transfer, it should be true that + * 1) the TIMEOUT bit (SPP_STR.0) is clear + * 2) the READY bit (SPP_STR.7) is set + * returns 1 if okay + */ +static int ex_ppa_check_epp_status(int minor) +{ + char r; + inb(STAT); + r = inb(STAT); + + if (r & 1) { + outb(r, STAT); + outb(r&0xfe, STAT); + printk("timed out on port 0x%04x\n", BASE); + return 0; + } + if (!(r & 0x80)) { + return 0; + } + return 1; +} + + +Local int pcf_read (int minor, int adr) +{ + int ret; + a0(adr); + ex_ppa_check_epp_status(minor); + ret = inb(lpPort(4)); + return ret; +} + + +Local void pcf_write (int minor, int adr,char data) +{ + a0(adr); + ex_ppa_check_epp_status(minor); + outb(data,lpPort(4)); +} + + +Local int pcf_init (int minor) +{ + char ctrl,ecr; /* parallel port registers */ + + if (check_region(i2c_table[minor].base, 8) < 0 ) { + return -ENODEV; + } else { + request_region(i2c_table[minor].base, 8, "i2c (pcf-lp)"); + } + + /* This is the standard detection algorithm for ECP ports */ + /* now detect, what kind of parallel port we have: */ + ecr = inb(i2c_table[minor].base+0x402); + ctrl= inb(i2c_table[minor].base+0x002); + if (ecr!=ctrl) { /* Okay, we seem to have an extended port :) */ + if (check_region(i2c_table[minor].base + 0x400, 8) < 0 ) { + DEBE(printk( "i2c_init, port %#x: ext. ports already in use " + "using only standard driver.\n", + i2c_table[minor].base)); + return -ENODEV; + } else { + if ( (ecr & 0x03) != 0x01 ) { /* FIFO empty? */ + printk("FIFO not empty\n"); + return -ENODEV; + } + + outb(0x34,i2c_table[minor].base+0x402); + if (inb(i2c_table[minor].base+0x402) != 0x35 ){ /* bits 0,1 readonly */ + printk("bits 0,1 not rd/only"); + return -ENODEV; + } + /* Okay, now we should have an ECP-capable printer port */ + printk("i2c%d: ECP parallel port.\n",minor); + request_region(i2c_table[minor].base+0x0400, 3 , + "i2c (Extended Parallel port adapter)"); + outb(0x94 ,lpPort(0x402)); /* EPP mode */ + ctrl = inb(CTRL); + + if (i2c_table[minor].irq > 0){ /* enable int. */ + outb(inb(lpPort(0x402)|0x10), lpPort(0x402)); + outb(ctrl|0x10,CTRL); + } + + ex_ppa_check_epp_status(minor); + printk("status reads %2x\n",pcf_read(minor,1)); + ex_ppa_check_epp_status(minor); + printk("data reads %2x\n",pcf_read(minor,0)); + } + } else { + printk("i2c%d: Need EPP port. Sorry.\n",minor); + return -ENODEV; + } + /* Okay, now it's time to init my data structures */ + + return 0; +} + + +Local void pcf_exit (int minor) +{ + release_region( i2c_table[minor].base, 8 ); + release_region( i2c_table[minor].base + 0x0400, 3 ); +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +#if (PCFADAPS) > 1 +struct i2c_pcf_ops pcf_lp_ops = { + pcf_read, + pcf_write, + pcf_init, + pcf_exit +}; +#endif diff --git a/i2c/old-code/pcf8584.h b/i2c/old-code/pcf8584.h new file mode 100644 index 00000000..b3db0403 --- /dev/null +++ b/i2c/old-code/pcf8584.h @@ -0,0 +1,72 @@ +/* -------------------------------------------------------------------- */ +/* PCF 8584 global defines */ +/* -------------------------------------------------------------------- */ +/* Copyright (C) 19996 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* -------------------------------------------------------------------- */ +/* $Id: pcf8584.h,v 1.1 1998/07/29 08:09:51 i2c Exp $ + * $Log: pcf8584.h,v $ + * Revision 1.1 1998/07/29 08:09:51 i2c + * Initial revision + * + */ + + +/* ----- Control register bits ---------------------------------------- */ +#define PCF_PIN 0x80 +#define PCF_ESO 0x40 +#define PCF_ES1 0x20 +#define PCF_ES2 0x10 +#define PCF_ENI 0x08 +#define PCF_STA 0x04 +#define PCF_STO 0x02 +#define PCF_ACK 0x01 + +/* ----- Status register bits ----------------------------------------- */ +/*#define PCF_PIN 0x80 as above*/ + +#define PCF_INI 0x40 /* 1 if not initialized */ +#define PCF_STS 0x20 +#define PCF_BER 0x10 +#define PCF_AD0 0x08 +#define PCF_LRB 0x08 +#define PCF_AAS 0x04 +#define PCF_LAB 0x02 +#define PCF_BB 0x01 + +/* ----- Chip clock frequencies --------------------------------------- */ +#define PCF_CLK3 0x00 +#define PCF_CLK443 0x10 +#define PCF_CLK6 0x14 +#define PCF_CLK8 0x18 +#define PCF_CLK12 0x1c + +/* ----- transmission frequencies ------------------------------------- */ +#define PCF_TRNS90 0x00 /* 90 kHz */ +#define PCF_TRNS45 0x01 /* 45 kHz */ +#define PCF_TRNS11 0x02 /* 11 kHz */ +#define PCF_TRNS15 0x03 /* 1.5 kHz */ + + +/* ----- Access to internal registers according to ES1,ES2 ------------ */ +/* they are mapped to the data port ( a0 = 0 ) */ +/* available when ESO == 0 : */ + +#define PCF_OWNADR 0 +#define PCF_INTREG PCF_ES2 +#define PCF_CLKREG PCF_ES1 + +