Content-Security-Policy: default-src 'none'
Content-Type: text/plain; charset=UTF-8
Content-Length: 3921
Content-Disposition: inline; filename="950-0107-i2c-bcm2835-Protect-against-unexpected-TXW-RXR-inter.patch"
Last-Modified: Sun, 28 Jul 2024 00:17:19 GMT
Expires: Wed, 26 Jul 2034 00:17:19 GMT
ETag: "6646d77944e2ac6969f9506cac8dfe1cc74282a8"

From 8feb8081c74d15ce368baa42981ca98e77800c03 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
Date: Fri, 23 Sep 2016 18:24:38 +0200
Subject: [PATCH] i2c: bcm2835: Protect against unexpected TXW/RXR interrupts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

If an unexpected TXW or RXR interrupt occurs (msg_buf_remaining == 0),
the driver has no way to fill/drain the FIFO to stop the interrupts.
In this case the controller has to be disabled and the transfer
completed to avoid hang.

(CLKT | ERR) and DONE interrupts are completed in their own paths, and
the controller is disabled in the transfer function after completion.
Unite the code paths and do disabling inside the interrupt routine.

Clear interrupt status bits in the united completion path instead of
trying to do it on every interrupt which isn't necessary.
Only CLKT, ERR and DONE can be cleared that way.

Add the status value to the error value in case of TXW/RXR errors to
distinguish them from the other S_LEN error.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
---
 drivers/i2c/busses/i2c-bcm2835.c | 40 +++++++++++++++++++++++++++++++---------
 1 file changed, 31 insertions(+), 9 deletions(-)

--- a/drivers/i2c/busses/i2c-bcm2835.c
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -50,8 +50,6 @@
 #define BCM2835_I2C_S_CLKT	BIT(9)
 #define BCM2835_I2C_S_LEN	BIT(10) /* Fake bit for SW error reporting */
 
-#define BCM2835_I2C_BITMSK_S	0x03FF
-
 #define BCM2835_I2C_CDIV_MIN	0x0002
 #define BCM2835_I2C_CDIV_MAX	0xFFFE
 
@@ -111,20 +109,26 @@ static void bcm2835_drain_rxfifo(struct
 	}
 }
 
+/*
+ * Note about I2C_C_CLEAR on error:
+ * The I2C_C_CLEAR on errors will take some time to resolve -- if you were in
+ * non-idle state and I2C_C_READ, it sets an abort_rx flag and runs through
+ * the state machine to send a NACK and a STOP. Since we're setting CLEAR
+ * without I2CEN, that NACK will be hanging around queued up for next time
+ * we start the engine.
+ */
+
 static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data)
 {
 	struct bcm2835_i2c_dev *i2c_dev = data;
 	u32 val, err;
 
 	val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
-	val &= BCM2835_I2C_BITMSK_S;
-	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val);
 
 	err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR);
 	if (err) {
 		i2c_dev->msg_err = err;
-		complete(&i2c_dev->completion);
-		return IRQ_HANDLED;
+		goto complete;
 	}
 
 	if (val & BCM2835_I2C_S_DONE) {
@@ -137,21 +141,38 @@ static irqreturn_t bcm2835_i2c_isr(int t
 			i2c_dev->msg_err = BCM2835_I2C_S_LEN;
 		else
 			i2c_dev->msg_err = 0;
-		complete(&i2c_dev->completion);
-		return IRQ_HANDLED;
+		goto complete;
 	}
 
 	if (val & BCM2835_I2C_S_TXW) {
+		if (!i2c_dev->msg_buf_remaining) {
+			i2c_dev->msg_err = val | BCM2835_I2C_S_LEN;
+			goto complete;
+		}
+
 		bcm2835_fill_txfifo(i2c_dev);
 		return IRQ_HANDLED;
 	}
 
 	if (val & BCM2835_I2C_S_RXR) {
+		if (!i2c_dev->msg_buf_remaining) {
+			i2c_dev->msg_err = val | BCM2835_I2C_S_LEN;
+			goto complete;
+		}
+
 		bcm2835_drain_rxfifo(i2c_dev);
 		return IRQ_HANDLED;
 	}
 
 	return IRQ_NONE;
+
+complete:
+	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
+	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, BCM2835_I2C_S_CLKT |
+			   BCM2835_I2C_S_ERR | BCM2835_I2C_S_DONE);
+	complete(&i2c_dev->completion);
+
+	return IRQ_HANDLED;
 }
 
 static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev,
@@ -181,8 +202,9 @@ static int bcm2835_i2c_xfer_msg(struct b
 
 	time_left = wait_for_completion_timeout(&i2c_dev->completion,
 						BCM2835_I2C_TIMEOUT);
-	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
 	if (!time_left) {
+		bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C,
+				   BCM2835_I2C_C_CLEAR);
 		dev_err(i2c_dev->dev, "i2c transfer timed out\n");
 		return -ETIMEDOUT;
 	}
649f08b6b5f87'>brcm2708</a>/<a href='/cgit.cgi/openwrt/upstream/tree/target/linux/brcm2708/patches-4.9?id=e80ab48777d83d8c24fcd397a85649f08b6b5f87'>patches-4.9</a>/<a href='/cgit.cgi/openwrt/upstream/tree/target/linux/brcm2708/patches-4.9/950-0198-bcm2835-gpio-exp-Driver-for-GPIO-expander-via-mailbo.patch?id=e80ab48777d83d8c24fcd397a85649f08b6b5f87'>950-0198-bcm2835-gpio-exp-Driver-for-GPIO-expander-via-mailbo.patch</a></div><div class='content'>blob: 3d09d1dc3f5619902aa9c9e0a0f7213c0d36d096 (<a href='/cgit.cgi/openwrt/upstream/plain/target/linux/brcm2708/patches-4.9/950-0198-bcm2835-gpio-exp-Driver-for-GPIO-expander-via-mailbo.patch?id=e80ab48777d83d8c24fcd397a85649f08b6b5f87'>plain</a>)
<table summary='blob content' class='blob'>
<tr><td class='linenumbers'><pre><a id='n1' href='#n1'>1</a>
<a id='n2' href='#n2'>2</a>
<a id='n3' href='#n3'>3</a>
<a id='n4' href='#n4'>4</a>
<a id='n5' href='#n5'>5</a>
<a id='n6' href='#n6'>6</a>
<a id='n7' href='#n7'>7</a>
<a id='n8' href='#n8'>8</a>
<a id='n9' href='#n9'>9</a>
<a id='n10' href='#n10'>10</a>
<a id='n11' href='#n11'>11</a>
<a id='n12' href='#n12'>12</a>
<a id='n13' href='#n13'>13</a>
<a id='n14' href='#n14'>14</a>
<a id='n15' href='#n15'>15</a>
<a id='n16' href='#n16'>16</a>
<a id='n17' href='#n17'>17</a>
<a id='n18' href='#n18'>18</a>
<a id='n19' href='#n19'>19</a>
<a id='n20' href='#n20'>20</a>
<a id='n21' href='#n21'>21</a>
<a id='n22' href='#n22'>22</a>
<a id='n23' href='#n23'>23</a>
<a id='n24' href='#n24'>24</a>
<a id='n25' href='#n25'>25</a>
<a id='n26' href='#n26'>26</a>
<a id='n27' href='#n27'>27</a>
<a id='n28' href='#n28'>28</a>
<a id='n29' href='#n29'>29</a>
<a id='n30' href='#n30'>30</a>
<a id='n31' href='#n31'>31</a>
<a id='n32' href='#n32'>32</a>
<a id='n33' href='#n33'>33</a>
<a id='n34' href='#n34'>34</a>
<a id='n35' href='#n35'>35</a>
<a id='n36' href='#n36'>36</a>
<a id='n37' href='#n37'>37</a>
<a id='n38' href='#n38'>38</a>
<a id='n39' href='#n39'>39</a>
<a id='n40' href='#n40'>40</a>
<a id='n41' href='#n41'>41</a>
<a id='n42' href='#n42'>42</a>
<a id='n43' href='#n43'>43</a>
<a id='n44' href='#n44'>44</a>
<a id='n45' href='#n45'>45</a>
<a id='n46' href='#n46'>46</a>
<a id='n47' href='#n47'>47</a>
<a id='n48' href='#n48'>48</a>
<a id='n49' href='#n49'>49</a>
<a id='n50' href='#n50'>50</a>
<a id='n51' href='#n51'>51</a>
<a id='n52' href='#n52'>52</a>
<a id='n53' href='#n53'>53</a>
<a id='n54' href='#n54'>54</a>
<a id='n55' href='#n55'>55</a>
<a id='n56' href='#n56'>56</a>
<a id='n57' href='#n57'>57</a>
<a id='n58' href='#n58'>58</a>
<a id='n59' href='#n59'>59</a>
<a id='n60' href='#n60'>60</a>
<a id='n61' href='#n61'>61</a>
<a id='n62' href='#n62'>62</a>
<a id='n63' href='#n63'>63</a>
<a id='n64' href='#n64'>64</a>
<a id='n65' href='#n65'>65</a>
<a id='n66' href='#n66'>66</a>
<a id='n67' href='#n67'>67</a>
<a id='n68' href='#n68'>68</a>
<a id='n69' href='#n69'>69</a>
<a id='n70' href='#n70'>70</a>
<a id='n71' href='#n71'>71</a>
<a id='n72' href='#n72'>72</a>
<a id='n73' href='#n73'>73</a>
<a id='n74' href='#n74'>74</a>
<a id='n75' href='#n75'>75</a>
<a id='n76' href='#n76'>76</a>
<a id='n77' href='#n77'>77</a>
<a id='n78' href='#n78'>78</a>
<a id='n79' href='#n79'>79</a>
<a id='n80' href='#n80'>80</a>
<a id='n81' href='#n81'>81</a>
<a id='n82' href='#n82'>82</a>
<a id='n83' href='#n83'>83</a>
<a id='n84' href='#n84'>84</a>
<a id='n85' href='#n85'>85</a>
<a id='n86' href='#n86'>86</a>
<a id='n87' href='#n87'>87</a>
<a id='n88' href='#n88'>88</a>
<a id='n89' href='#n89'>89</a>
<a id='n90' href='#n90'>90</a>
<a id='n91' href='#n91'>91</a>
<a id='n92' href='#n92'>92</a>
<a id='n93' href='#n93'>93</a>
<a id='n94' href='#n94'>94</a>
<a id='n95' href='#n95'>95</a>
<a id='n96' href='#n96'>96</a>
<a id='n97' href='#n97'>97</a>
<a id='n98' href='#n98'>98</a>
<a id='n99' href='#n99'>99</a>
<a id='n100' href='#n100'>100</a>
<a id='n101' href='#n101'>101</a>
<a id='n102' href='#n102'>102</a>
<a id='n103' href='#n103'>103</a>
<a id='n104' href='#n104'>104</a>
<a id='n105' href='#n105'>105</a>
<a id='n106' href='#n106'>106</a>
<a id='n107' href='#n107'>107</a>
<a id='n108' href='#n108'>108</a>
<a id='n109' href='#n109'>109</a>
<a id='n110' href='#n110'>110</a>
<a id='n111' href='#n111'>111</a>
<a id='n112' href='#n112'>112</a>
<a id='n113' href='#n113'>113</a>
<a id='n114' href='#n114'>114</a>
<a id='n115' href='#n115'>115</a>
<a id='n116' href='#n116'>116</a>
<a id='n117' href='#n117'>117</a>
<a id='n118' href='#n118'>118</a>
<a id='n119' href='#n119'>119</a>
<a id='n120' href='#n120'>120</a>
<a id='n121' href='#n121'>121</a>
<a id='n122' href='#n122'>122</a>
<a id='n123' href='#n123'>123</a>
<a id='n124' href='#n124'>124</a>
<a id='n125' href='#n125'>125</a>
<a id='n126' href='#n126'>126</a>
<a id='n127' href='#n127'>127</a>
<a id='n128' href='#n128'>128</a>
<a id='n129' href='#n129'>129</a>
<a id='n130' href='#n130'>130</a>
<a id='n131' href='#n131'>131</a>
<a id='n132' href='#n132'>132</a>
<a id='n133' href='#n133'>133</a>
<a id='n134' href='#n134'>134</a>
<a id='n135' href='#n135'>135</a>
<a id='n136' href='#n136'>136</a>
<a id='n137' href='#n137'>137</a>
<a id='n138' href='#n138'>138</a>
<a id='n139' href='#n139'>139</a>
<a id='n140' href='#n140'>140</a>
<a id='n141' href='#n141'>141</a>
<a id='n142' href='#n142'>142</a>
<a id='n143' href='#n143'>143</a>
<a id='n144' href='#n144'>144</a>
<a id='n145' href='#n145'>145</a>
<a id='n146' href='#n146'>146</a>
<a id='n147' href='#n147'>147</a>
<a id='n148' href='#n148'>148</a>
<a id='n149' href='#n149'>149</a>
<a id='n150' href='#n150'>150</a>
<a id='n151' href='#n151'>151</a>
<a id='n152' href='#n152'>152</a>
<a id='n153' href='#n153'>153</a>
<a id='n154' href='#n154'>154</a>
<a id='n155' href='#n155'>155</a>
<a id='n156' href='#n156'>156</a>
<a id='n157' href='#n157'>157</a>
<a id='n158' href='#n158'>158</a>
<a id='n159' href='#n159'>159</a>
<a id='n160' href='#n160'>160</a>
<a id='n161' href='#n161'>161</a>
<a id='n162' href='#n162'>162</a>
<a id='n163' href='#n163'>163</a>
<a id='n164' href='#n164'>164</a>
<a id='n165' href='#n165'>165</a>
<a id='n166' href='#n166'>166</a>
<a id='n167' href='#n167'>167</a>
<a id='n168' href='#n168'>168</a>
<a id='n169' href='#n169'>169</a>
<a id='n170' href='#n170'>170</a>
<a id='n171' href='#n171'>171</a>
<a id='n172' href='#n172'>172</a>
<a id='n173' href='#n173'>173</a>
<a id='n174' href='#n174'>174</a>
<a id='n175' href='#n175'>175</a>
<a id='n176' href='#n176'>176</a>
<a id='n177' href='#n177'>177</a>
<a id='n178' href='#n178'>178</a>
<a id='n179' href='#n179'>179</a>
<a id='n180' href='#n180'>180</a>
<a id='n181' href='#n181'>181</a>
<a id='n182' href='#n182'>182</a>
<a id='n183' href='#n183'>183</a>
<a id='n184' href='#n184'>184</a>
<a id='n185' href='#n185'>185</a>
<a id='n186' href='#n186'>186</a>
<a id='n187' href='#n187'>187</a>
<a id='n188' href='#n188'>188</a>
<a id='n189' href='#n189'>189</a>
<a id='n190' href='#n190'>190</a>
<a id='n191' href='#n191'>191</a>
<a id='n192' href='#n192'>192</a>
<a id='n193' href='#n193'>193</a>
<a id='n194' href='#n194'>194</a>
<a id='n195' href='#n195'>195</a>
<a id='n196' href='#n196'>196</a>
<a id='n197' href='#n197'>197</a>
<a id='n198' href='#n198'>198</a>
<a id='n199' href='#n199'>199</a>
<a id='n200' href='#n200'>200</a>
<a id='n201' href='#n201'>201</a>
<a id='n202' href='#n202'>202</a>
<a id='n203' href='#n203'>203</a>
<a id='n204' href='#n204'>204</a>
<a id='n205' href='#n205'>205</a>
<a id='n206' href='#n206'>206</a>
<a id='n207' href='#n207'>207</a>
<a id='n208' href='#n208'>208</a>
<a id='n209' href='#n209'>209</a>
<a id='n210' href='#n210'>210</a>
<a id='n211' href='#n211'>211</a>
<a id='n212' href='#n212'>212</a>
<a id='n213' href='#n213'>213</a>
<a id='n214' href='#n214'>214</a>
<a id='n215' href='#n215'>215</a>
<a id='n216' href='#n216'>216</a>
<a id='n217' href='#n217'>217</a>
<a id='n218' href='#n218'>218</a>
<a id='n219' href='#n219'>219</a>
<a id='n220' href='#n220'>220</a>
<a id='n221' href='#n221'>221</a>
<a id='n222' href='#n222'>222</a>
<a id='n223' href='#n223'>223</a>
<a id='n224' href='#n224'>224</a>
<a id='n225' href='#n225'>225</a>
<a id='n226' href='#n226'>226</a>
<a id='n227' href='#n227'>227</a>
<a id='n228' href='#n228'>228</a>
<a id='n229' href='#n229'>229</a>
<a id='n230' href='#n230'>230</a>
<a id='n231' href='#n231'>231</a>
<a id='n232' href='#n232'>232</a>
<a id='n233' href='#n233'>233</a>
<a id='n234' href='#n234'>234</a>
<a id='n235' href='#n235'>235</a>
<a id='n236' href='#n236'>236</a>
<a id='n237' href='#n237'>237</a>
<a id='n238' href='#n238'>238</a>
<a id='n239' href='#n239'>239</a>
<a id='n240' href='#n240'>240</a>
<a id='n241' href='#n241'>241</a>
<a id='n242' href='#n242'>242</a>
<a id='n243' href='#n243'>243</a>
<a id='n244' href='#n244'>244</a>
<a id='n245' href='#n245'>245</a>
<a id='n246' href='#n246'>246</a>
<a id='n247' href='#n247'>247</a>
<a id='n248' href='#n248'>248</a>
<a id='n249' href='#n249'>249</a>
<a id='n250' href='#n250'>250</a>
<a id='n251' href='#n251'>251</a>
<a id='n252' href='#n252'>252</a>
<a id='n253' href='#n253'>253</a>
<a id='n254' href='#n254'>254</a>
<a id='n255' href='#n255'>255</a>
<a id='n256' href='#n256'>256</a>
<a id='n257' href='#n257'>257</a>
<a id='n258' href='#n258'>258</a>
<a id='n259' href='#n259'>259</a>
<a id='n260' href='#n260'>260</a>
<a id='n261' href='#n261'>261</a>
<a id='n262' href='#n262'>262</a>
<a id='n263' href='#n263'>263</a>
<a id='n264' href='#n264'>264</a>
<a id='n265' href='#n265'>265</a>
<a id='n266' href='#n266'>266</a>
<a id='n267' href='#n267'>267</a>
<a id='n268' href='#n268'>268</a>
<a id='n269' href='#n269'>269</a>
<a id='n270' href='#n270'>270</a>
<a id='n271' href='#n271'>271</a>
<a id='n272' href='#n272'>272</a>
<a id='n273' href='#n273'>273</a>
<a id='n274' href='#n274'>274</a>
<a id='n275' href='#n275'>275</a>
<a id='n276' href='#n276'>276</a>
<a id='n277' href='#n277'>277</a>
<a id='n278' href='#n278'>278</a>
<a id='n279' href='#n279'>279</a>
<a id='n280' href='#n280'>280</a>
<a id='n281' href='#n281'>281</a>
<a id='n282' href='#n282'>282</a>
<a id='n283' href='#n283'>283</a>
<a id='n284' href='#n284'>284</a>
<a id='n285' href='#n285'>285</a>
<a id='n286' href='#n286'>286</a>
<a id='n287' href='#n287'>287</a>
<a id='n288' href='#n288'>288</a>
<a id='n289' href='#n289'>289</a>
<a id='n290' href='#n290'>290</a>
<a id='n291' href='#n291'>291</a>
<a id='n292' href='#n292'>292</a>
<a id='n293' href='#n293'>293</a>
<a id='n294' href='#n294'>294</a>
<a id='n295' href='#n295'>295</a>
<a id='n296' href='#n296'>296</a>
<a id='n297' href='#n297'>297</a>
<a id='n298' href='#n298'>298</a>
<a id='n299' href='#n299'>299</a>
<a id='n300' href='#n300'>300</a>
<a id='n301' href='#n301'>301</a>
<a id='n302' href='#n302'>302</a>
<a id='n303' href='#n303'>303</a>
<a id='n304' href='#n304'>304</a>
<a id='n305' href='#n305'>305</a>
<a id='n306' href='#n306'>306</a>
<a id='n307' href='#n307'>307</a>
<a id='n308' href='#n308'>308</a>
<a id='n309' href='#n309'>309</a>
<a id='n310' href='#n310'>310</a>
<a id='n311' href='#n311'>311</a>
<a id='n312' href='#n312'>312</a>
<a id='n313' href='#n313'>313</a>
<a id='n314' href='#n314'>314</a>
<a id='n315' href='#n315'>315</a>
<a id='n316' href='#n316'>316</a>
<a id='n317' href='#n317'>317</a>
<a id='n318' href='#n318'>318</a>
<a id='n319' href='#n319'>319</a>
<a id='n320' href='#n320'>320</a>
<a id='n321' href='#n321'>321</a>
<a id='n322' href='#n322'>322</a>
<a id='n323' href='#n323'>323</a>
<a id='n324' href='#n324'>324</a>
<a id='n325' href='#n325'>325</a>
<a id='n326' href='#n326'>326</a>
<a id='n327' href='#n327'>327</a>
<a id='n328' href='#n328'>328</a>
<a id='n329' href='#n329'>329</a>
<a id='n330' href='#n330'>330</a>
<a id='n331' href='#n331'>331</a>
<a id='n332' href='#n332'>332</a>
<a id='n333' href='#n333'>333</a>
<a id='n334' href='#n334'>334</a>
<a id='n335' href='#n335'>335</a>
<a id='n336' href='#n336'>336</a>
<a id='n337' href='#n337'>337</a>
<a id='n338' href='#n338'>338</a>
<a id='n339' href='#n339'>339</a>
<a id='n340' href='#n340'>340</a>
<a id='n341' href='#n341'>341</a>
<a id='n342' href='#n342'>342</a>
<a id='n343' href='#n343'>343</a>
<a id='n344' href='#n344'>344</a>
<a id='n345' href='#n345'>345</a>
<a id='n346' href='#n346'>346</a>
<a id='n347' href='#n347'>347</a>
<a id='n348' href='#n348'>348</a>
<a id='n349' href='#n349'>349</a>
<a id='n350' href='#n350'>350</a>
<a id='n351' href='#n351'>351</a>
<a id='n352' href='#n352'>352</a>
<a id='n353' href='#n353'>353</a>
<a id='n354' href='#n354'>354</a>
<a id='n355' href='#n355'>355</a>
<a id='n356' href='#n356'>356</a>
<a id='n357' href='#n357'>357</a>
<a id='n358' href='#n358'>358</a>
<a id='n359' href='#n359'>359</a>
<a id='n360' href='#n360'>360</a>
<a id='n361' href='#n361'>361</a>
<a id='n362' href='#n362'>362</a>
<a id='n363' href='#n363'>363</a>
<a id='n364' href='#n364'>364</a>
<a id='n365' href='#n365'>365</a>
<a id='n366' href='#n366'>366</a>
<a id='n367' href='#n367'>367</a>
<a id='n368' href='#n368'>368</a>
<a id='n369' href='#n369'>369</a>
<a id='n370' href='#n370'>370</a>
<a id='n371' href='#n371'>371</a>
<a id='n372' href='#n372'>372</a>
<a id='n373' href='#n373'>373</a>
<a id='n374' href='#n374'>374</a>
<a id='n375' href='#n375'>375</a>
<a id='n376' href='#n376'>376</a>
<a id='n377' href='#n377'>377</a>
<a id='n378' href='#n378'>378</a>
<a id='n379' href='#n379'>379</a>
<a id='n380' href='#n380'>380</a>
<a id='n381' href='#n381'>381</a>
<a id='n382' href='#n382'>382</a>
<a id='n383' href='#n383'>383</a>
<a id='n384' href='#n384'>384</a>
<a id='n385' href='#n385'>385</a>
<a id='n386' href='#n386'>386</a>
<a id='n387' href='#n387'>387</a>
<a id='n388' href='#n388'>388</a>
<a id='n389' href='#n389'>389</a>
<a id='n390' href='#n390'>390</a>
<a id='n391' href='#n391'>391</a>
<a id='n392' href='#n392'>392</a>
<a id='n393' href='#n393'>393</a>
<a id='n394' href='#n394'>394</a>
<a id='n395' href='#n395'>395</a>
<a id='n396' href='#n396'>396</a>
<a id='n397' href='#n397'>397</a>
<a id='n398' href='#n398'>398</a>
<a id='n399' href='#n399'>399</a>
<a id='n400' href='#n400'>400</a>
<a id='n401' href='#n401'>401</a>
<a id='n402' href='#n402'>402</a>
<a id='n403' href='#n403'>403</a>
<a id='n404' href='#n404'>404</a>
<a id='n405' href='#n405'>405</a>
</pre></td>
<td class='lines'><pre><code><style>pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span>From 2f8f62fbd42a55825da437faba4b6164f2e0bee9 Mon Sep 17 00:00:00 2001
From: Dave Stevenson &lt;dave.stevenson@raspberrypi.org&gt;
Date: Mon, 20 Feb 2017 17:01:21 +0000
Subject: [PATCH] bcm2835-gpio-exp: Driver for GPIO expander via mailbox
 service

Pi3 and Compute Module 3 have a GPIO expander that the
VPU communicates with.
There is a mailbox service that now allows control of this
expander, so add a kernel driver that can make use of it.

Pwr_led node added to device-tree for Pi3.

Signed-off-by: Dave Stevenson &lt;dave.stevenson@raspberrypi.org&gt;
<span class="gd">---</span>
 arch/arm/boot/dts/bcm2710-rpi-3-b.dts      |  22 +++
 arch/arm/boot/dts/bcm2710-rpi-cm3.dts      |  10 +-
 arch/arm/configs/bcm2709_defconfig         |   1 +
 drivers/gpio/Kconfig                       |   7 +
 drivers/gpio/Makefile                      |   1 +
 drivers/gpio/gpio-bcm-exp.c                | 256 +++++++++++++++++++++++++++++
 include/soc/bcm2835/raspberrypi-firmware.h |   4 +
 7 files changed, 300 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpio/gpio-bcm-exp.c

<span class="gd">--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts</span>
<span class="gi">+++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts</span>
<span class="gu">@@ -96,6 +96,14 @@</span>
 		firmware = &lt;&amp;firmware&gt;;
 		status = &quot;okay&quot;;
 	};
<span class="gi">+</span>
<span class="gi">+	expgpio: expgpio {</span>
<span class="gi">+		compatible = &quot;brcm,bcm2835-expgpio&quot;;</span>
<span class="gi">+		gpio-controller;</span>
<span class="gi">+		#gpio-cells = &lt;2&gt;;</span>
<span class="gi">+		firmware = &lt;&amp;firmware&gt;;</span>
<span class="gi">+		status = &quot;okay&quot;;</span>
<span class="gi">+	};</span>
 };
 
 &amp;fb {
<span class="gu">@@ -163,6 +171,16 @@</span>
 		linux,default-trigger = &quot;mmc0&quot;;
 		gpios = &lt;&amp;virtgpio 0 0&gt;;
 	};
<span class="gi">+</span>
<span class="gi">+	pwr_led: pwr {</span>
<span class="gi">+		label = &quot;led1&quot;;</span>
<span class="gi">+		linux,default-trigger = &quot;input&quot;;</span>
<span class="gi">+		gpios = &lt;&amp;expgpio 7 GPIO_ACTIVE_LOW&gt;;</span>
<span class="gi">+	};</span>
<span class="gi">+};</span>
<span class="gi">+</span>
<span class="gi">+&amp;hdmi {</span>
<span class="gi">+	hpd-gpios = &lt;&amp;expgpio 4 GPIO_ACTIVE_LOW&gt;;</span>
 };
 
 &amp;audio {
<span class="gu">@@ -193,6 +211,10 @@</span>
 		act_led_activelow = &lt;&amp;act_led&gt;,&quot;gpios:8&quot;;
 		act_led_trigger = &lt;&amp;act_led&gt;,&quot;linux,default-trigger&quot;;
 
<span class="gi">+		pwr_led_gpio = &lt;&amp;pwr_led&gt;,&quot;gpios:4&quot;;</span>
<span class="gi">+		pwr_led_activelow = &lt;&amp;pwr_led&gt;,&quot;gpios:8&quot;;</span>
<span class="gi">+		pwr_led_trigger = &lt;&amp;pwr_led&gt;,&quot;linux,default-trigger&quot;;</span>
<span class="gi">+</span>
 		audio = &lt;&amp;audio&gt;,&quot;status&quot;;
 		watchdog = &lt;&amp;watchdog&gt;,&quot;status&quot;;
 		random = &lt;&amp;random&gt;,&quot;status&quot;;
<span class="gd">--- a/arch/arm/boot/dts/bcm2710-rpi-cm3.dts</span>
<span class="gi">+++ b/arch/arm/boot/dts/bcm2710-rpi-cm3.dts</span>
<span class="gu">@@ -65,6 +65,14 @@</span>
 		firmware = &lt;&amp;firmware&gt;;
 		status = &quot;okay&quot;;
 	};
<span class="gi">+</span>
<span class="gi">+	expgpio: expgpio {</span>
<span class="gi">+		compatible = &quot;brcm,bcm2835-expgpio&quot;;</span>
<span class="gi">+		gpio-controller;</span>
<span class="gi">+		#gpio-cells = &lt;2&gt;;</span>
<span class="gi">+		firmware = &lt;&amp;firmware&gt;;</span>
<span class="gi">+		status = &quot;okay&quot;;</span>
<span class="gi">+	};</span>
 };
 
 &amp;fb {
<span class="gu">@@ -123,7 +131,7 @@</span>
 };
 
 &amp;hdmi {
<span class="gd">-	hpd-gpios = &lt;&amp;gpio 46 GPIO_ACTIVE_LOW&gt;;</span>
<span class="gi">+	hpd-gpios = &lt;&amp;expgpio 0 GPIO_ACTIVE_LOW&gt;;</span>
 };
 
 &amp;audio {
<span class="gd">--- a/arch/arm/configs/bcm2709_defconfig</span>
<span class="gi">+++ b/arch/arm/configs/bcm2709_defconfig</span>
<span class="gu">@@ -625,6 +625,7 @@ CONFIG_PPS=m</span>
 CONFIG_PPS_CLIENT_LDISC=m
 CONFIG_PPS_CLIENT_GPIO=m
 CONFIG_GPIO_SYSFS=y
<span class="gi">+CONFIG_GPIO_BCM_EXP=y</span>
 CONFIG_GPIO_BCM_VIRT=y
 CONFIG_GPIO_ARIZONA=m
 CONFIG_GPIO_STMPE=y
<span class="gd">--- a/drivers/gpio/Kconfig</span>
<span class="gi">+++ b/drivers/gpio/Kconfig</span>
<span class="gu">@@ -128,6 +128,13 @@ config GPIO_AXP209</span>
 	help
 	  Say yes to enable GPIO support for the AXP209 PMIC
 
<span class="gi">+config GPIO_BCM_EXP</span>
<span class="gi">+	bool &quot;Broadcom Exp GPIO&quot;</span>
<span class="gi">+	depends on OF_GPIO &amp;&amp; RASPBERRYPI_FIRMWARE &amp;&amp; (ARCH_BCM2835 || COMPILE_TEST)</span>
<span class="gi">+	help</span>
<span class="gi">+	  Turn on GPIO support for Broadcom chips using the firmware mailbox</span>
<span class="gi">+	  to communicate with VideoCore on BCM283x chips.</span>
<span class="gi">+</span>
 config GPIO_BCM_KONA
 	bool &quot;Broadcom Kona GPIO&quot;
 	depends on OF_GPIO &amp;&amp; (ARCH_BCM_MOBILE || COMPILE_TEST)
<span class="gd">--- a/drivers/gpio/Makefile</span>
<span class="gi">+++ b/drivers/gpio/Makefile</span>
<span class="gu">@@ -30,6 +30,7 @@ obj-$(CONFIG_GPIO_ARIZONA)	+= gpio-arizo</span>
 obj-$(CONFIG_GPIO_ATH79)	+= gpio-ath79.o
 obj-$(CONFIG_GPIO_ASPEED)	+= gpio-aspeed.o
 obj-$(CONFIG_GPIO_AXP209)	+= gpio-axp209.o
<span class="gi">+obj-$(CONFIG_GPIO_BCM_EXP)	+= gpio-bcm-exp.o</span>
 obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BCM_VIRT)	+= gpio-bcm-virt.o
 obj-$(CONFIG_GPIO_BRCMSTB)	+= gpio-brcmstb.o
<span class="gd">--- /dev/null</span>
<span class="gi">+++ b/drivers/gpio/gpio-bcm-exp.c</span>
<span class="gu">@@ -0,0 +1,256 @@</span>
<span class="gi">+/*</span>
<span class="gi">+ *  Broadcom expander GPIO driver</span>
<span class="gi">+ *</span>
<span class="gi">+ *  Uses the firmware mailbox service to communicate with the</span>
<span class="gi">+ *  GPIO expander on the VPU.</span>
<span class="gi">+ *</span>
<span class="gi">+ *  Copyright (C) 2017 Raspberry Pi Trading Ltd.</span>
<span class="gi">+ *</span>
<span class="gi">+ *  Author: Dave Stevenson &lt;dave.stevenson@raspberrypi.org&gt;</span>
<span class="gi">+ *  Based on gpio-bcm-virt.c by Dom Cobley &lt;popcornmix@gmail.com&gt;</span>
<span class="gi">+ *</span>
<span class="gi">+ * This program is free software; you can redistribute it and/or modify</span>
<span class="gi">+ * it under the terms of the GNU General Public License as published by</span>
<span class="gi">+ * the Free Software Foundation; either version 2 of the License, or</span>
<span class="gi">+ * (at your option) any later version.</span>
<span class="gi">+ */</span>
<span class="gi">+</span>
<span class="gi">+#include &lt;linux/err.h&gt;</span>
<span class="gi">+#include &lt;linux/gpio.h&gt;</span>
<span class="gi">+#include &lt;linux/module.h&gt;</span>
<span class="gi">+#include &lt;linux/platform_device.h&gt;</span>
<span class="gi">+#include &lt;linux/dma-mapping.h&gt;</span>
<span class="gi">+#include &lt;soc/bcm2835/raspberrypi-firmware.h&gt;</span>
<span class="gi">+</span>
<span class="gi">+#define MODULE_NAME &quot;brcmexp-gpio&quot;</span>
<span class="gi">+#define NUM_GPIO 8</span>
<span class="gi">+</span>
<span class="gi">+struct brcmexp_gpio {</span>
<span class="gi">+	struct gpio_chip gc;</span>
<span class="gi">+	struct device *dev;</span>
<span class="gi">+	struct rpi_firmware *fw;</span>
<span class="gi">+};</span>
<span class="gi">+</span>
<span class="gi">+struct gpio_set_config {</span>
<span class="gi">+	u32 gpio, direction, polarity, term_en, term_pull_up, state;</span>
<span class="gi">+};</span>
<span class="gi">+</span>
<span class="gi">+struct gpio_get_config {</span>
<span class="gi">+	u32 gpio, direction, polarity, term_en, term_pull_up;</span>
<span class="gi">+};</span>
<span class="gi">+</span>
<span class="gi">+struct gpio_get_set_state {</span>
<span class="gi">+	u32 gpio, state;</span>
<span class="gi">+};</span>
<span class="gi">+</span>
<span class="gi">+static int brcmexp_gpio_get_polarity(struct gpio_chip *gc, unsigned int off)</span>
<span class="gi">+{</span>
<span class="gi">+	struct brcmexp_gpio *gpio;</span>
<span class="gi">+	struct gpio_get_config get;</span>
<span class="gi">+	int ret;</span>
<span class="gi">+</span>
<span class="gi">+	gpio = container_of(gc, struct brcmexp_gpio, gc);</span>
<span class="gi">+</span>
<span class="gi">+	get.gpio = off + gpio-&gt;gc.base;	/* GPIO to update */</span>
<span class="gi">+</span>
<span class="gi">+	ret = rpi_firmware_property(gpio-&gt;fw, RPI_FIRMWARE_GET_GPIO_CONFIG,</span>
<span class="gi">+				    &amp;get, sizeof(get));</span>
<span class="gi">+	if (ret) {</span>
<span class="gi">+		dev_err(gpio-&gt;dev,</span>
<span class="gi">+			&quot;Failed to get GPIO %u config (%d)\n&quot;, off, ret);</span>
<span class="gi">+		return ret;</span>
<span class="gi">+	}</span>
<span class="gi">+	return get.polarity;</span>
<span class="gi">+}</span>
<span class="gi">+</span>
<span class="gi">+static int brcmexp_gpio_dir_in(struct gpio_chip *gc, unsigned int off)</span>
<span class="gi">+{</span>
<span class="gi">+	struct brcmexp_gpio *gpio;</span>
<span class="gi">+	struct gpio_set_config set_in;</span>
<span class="gi">+	int ret;</span>
<span class="gi">+</span>
<span class="gi">+	gpio = container_of(gc, struct brcmexp_gpio, gc);</span>
<span class="gi">+</span>
<span class="gi">+	set_in.gpio = off + gpio-&gt;gc.base;	/* GPIO to update */</span>
<span class="gi">+	set_in.direction = 0;		/* Input */</span>
<span class="gi">+	set_in.polarity = brcmexp_gpio_get_polarity(gc, off);</span>
<span class="gi">+					/* Retain existing setting */</span>
<span class="gi">+	set_in.term_en = 0;		/* termination disabled */</span>
<span class="gi">+	set_in.term_pull_up = 0;	/* n/a as termination disabled */</span>
<span class="gi">+	set_in.state = 0;		/* n/a as configured as an input */</span>
<span class="gi">+</span>
<span class="gi">+	ret = rpi_firmware_property(gpio-&gt;fw, RPI_FIRMWARE_SET_GPIO_CONFIG,</span>
<span class="gi">+				    &amp;set_in, sizeof(set_in));</span>
<span class="gi">+	if (ret) {</span>
<span class="gi">+		dev_err(gpio-&gt;dev,</span>
<span class="gi">+			&quot;Failed to set GPIO %u to input (%d)\n&quot;,</span>
<span class="gi">+			off, ret);</span>
<span class="gi">+		return ret;</span>
<span class="gi">+	}</span>
<span class="gi">+	return 0;</span>
<span class="gi">+}</span>
<span class="gi">+</span>
<span class="gi">+static int brcmexp_gpio_dir_out(struct gpio_chip *gc, unsigned int off, int val)</span>
<span class="gi">+{</span>
<span class="gi">+	struct brcmexp_gpio *gpio;</span>
<span class="gi">+	struct gpio_set_config set_out;</span>
<span class="gi">+	int ret;</span>
<span class="gi">+</span>
<span class="gi">+	gpio = container_of(gc, struct brcmexp_gpio, gc);</span>
<span class="gi">+</span>
<span class="gi">+	set_out.gpio = off + gpio-&gt;gc.base;	/* GPIO to update */</span>
<span class="gi">+	set_out.direction = 1;		/* Output */</span>
<span class="gi">+	set_out.polarity = brcmexp_gpio_get_polarity(gc, off);</span>
<span class="gi">+					/* Retain existing setting */</span>
<span class="gi">+	set_out.term_en = 0;		/* n/a as an output */</span>
<span class="gi">+	set_out.term_pull_up = 0;	/* n/a as termination disabled */</span>
<span class="gi">+	set_out.state = val;		/* Output state */</span>
<span class="gi">+</span>
<span class="gi">+	ret = rpi_firmware_property(gpio-&gt;fw, RPI_FIRMWARE_SET_GPIO_CONFIG,</span>
<span class="gi">+				    &amp;set_out, sizeof(set_out));</span>
<span class="gi">+	if (ret) {</span>
<span class="gi">+		dev_err(gpio-&gt;dev,</span>
<span class="gi">+			&quot;Failed to set GPIO %u to output (%d)\n&quot;, off, ret);</span>
<span class="gi">+		return ret;</span>
<span class="gi">+	}</span>
<span class="gi">+	return 0;</span>
<span class="gi">+}</span>
<span class="gi">+</span>
<span class="gi">+static int brcmexp_gpio_get_direction(struct gpio_chip *gc, unsigned int off)</span>
<span class="gi">+{</span>
<span class="gi">+	struct brcmexp_gpio *gpio;</span>
<span class="gi">+	struct gpio_get_config get;</span>
<span class="gi">+	int ret;</span>
<span class="gi">+</span>
<span class="gi">+	gpio = container_of(gc, struct brcmexp_gpio, gc);</span>
<span class="gi">+</span>
<span class="gi">+	get.gpio = off + gpio-&gt;gc.base;	/* GPIO to update */</span>
<span class="gi">+</span>
<span class="gi">+	ret = rpi_firmware_property(gpio-&gt;fw, RPI_FIRMWARE_GET_GPIO_CONFIG,</span>
<span class="gi">+				    &amp;get, sizeof(get));</span>
<span class="gi">+	if (ret) {</span>
<span class="gi">+		dev_err(gpio-&gt;dev,</span>
<span class="gi">+			&quot;Failed to get GPIO %u config (%d)\n&quot;, off, ret);</span>
<span class="gi">+		return ret;</span>
<span class="gi">+	}</span>
<span class="gi">+	return get.direction ? GPIOF_DIR_OUT : GPIOF_DIR_IN;</span>
<span class="gi">+}</span>
<span class="gi">+</span>
<span class="gi">+static int brcmexp_gpio_get(struct gpio_chip *gc, unsigned int off)</span>
<span class="gi">+{</span>
<span class="gi">+	struct brcmexp_gpio *gpio;</span>
<span class="gi">+	struct gpio_get_set_state get;</span>
<span class="gi">+	int ret;</span>
<span class="gi">+</span>
<span class="gi">+	gpio = container_of(gc, struct brcmexp_gpio, gc);</span>
<span class="gi">+</span>
<span class="gi">+	get.gpio = off + gpio-&gt;gc.base;	/* GPIO to update */</span>
<span class="gi">+	get.state = 0;		/* storage for returned value */</span>
<span class="gi">+</span>
<span class="gi">+	ret = rpi_firmware_property(gpio-&gt;fw, RPI_FIRMWARE_GET_GPIO_STATE,</span>
<span class="gi">+					 &amp;get, sizeof(get));</span>
<span class="gi">+	if (ret) {</span>
<span class="gi">+		dev_err(gpio-&gt;dev,</span>
<span class="gi">+			&quot;Failed to get GPIO %u state (%d)\n&quot;, off, ret);</span>
<span class="gi">+		return ret;</span>
<span class="gi">+	}</span>
<span class="gi">+	return !!get.state;</span>
<span class="gi">+}</span>
<span class="gi">+</span>
<span class="gi">+static void brcmexp_gpio_set(struct gpio_chip *gc, unsigned int off, int val)</span>
<span class="gi">+{</span>
<span class="gi">+	struct brcmexp_gpio *gpio;</span>
<span class="gi">+	struct gpio_get_set_state set;</span>
<span class="gi">+	int ret;</span>
<span class="gi">+</span>
<span class="gi">+	gpio = container_of(gc, struct brcmexp_gpio, gc);</span>
<span class="gi">+</span>
<span class="gi">+	off += gpio-&gt;gc.base;</span>
<span class="gi">+</span>
<span class="gi">+	set.gpio = off + gpio-&gt;gc.base;	/* GPIO to update */</span>
<span class="gi">+	set.state = val;	/* Output state */</span>
<span class="gi">+</span>
<span class="gi">+	ret = rpi_firmware_property(gpio-&gt;fw, RPI_FIRMWARE_SET_GPIO_STATE,</span>
<span class="gi">+					 &amp;set, sizeof(set));</span>
<span class="gi">+	if (ret)</span>
<span class="gi">+		dev_err(gpio-&gt;dev,</span>
<span class="gi">+			&quot;Failed to set GPIO %u state (%d)\n&quot;, off, ret);</span>
<span class="gi">+}</span>
<span class="gi">+</span>
<span class="gi">+static int brcmexp_gpio_probe(struct platform_device *pdev)</span>
<span class="gi">+{</span>
<span class="gi">+	int err = 0;</span>
<span class="gi">+	struct device *dev = &amp;pdev-&gt;dev;</span>
<span class="gi">+	struct device_node *np = dev-&gt;of_node;</span>
<span class="gi">+	struct device_node *fw_node;</span>
<span class="gi">+	struct rpi_firmware *fw;</span>
<span class="gi">+	struct brcmexp_gpio *ucb;</span>
<span class="gi">+</span>
<span class="gi">+	fw_node = of_parse_phandle(np, &quot;firmware&quot;, 0);</span>
<span class="gi">+	if (!fw_node) {</span>
<span class="gi">+		dev_err(dev, &quot;Missing firmware node\n&quot;);</span>
<span class="gi">+		return -ENOENT;</span>
<span class="gi">+	}</span>
<span class="gi">+</span>
<span class="gi">+	fw = rpi_firmware_get(fw_node);</span>
<span class="gi">+	if (!fw)</span>
<span class="gi">+		return -EPROBE_DEFER;</span>
<span class="gi">+</span>
<span class="gi">+	ucb = devm_kzalloc(dev, sizeof(*ucb), GFP_KERNEL);</span>
<span class="gi">+	if (!ucb)</span>
<span class="gi">+		return -EINVAL;</span>
<span class="gi">+</span>
<span class="gi">+	ucb-&gt;fw = fw;</span>
<span class="gi">+	ucb-&gt;dev = dev;</span>
<span class="gi">+	ucb-&gt;gc.label = MODULE_NAME;</span>
<span class="gi">+	ucb-&gt;gc.owner = THIS_MODULE;</span>
<span class="gi">+	ucb-&gt;gc.of_node = np;</span>
<span class="gi">+	ucb-&gt;gc.base = 128;</span>
<span class="gi">+	ucb-&gt;gc.ngpio = NUM_GPIO;</span>
<span class="gi">+</span>
<span class="gi">+	ucb-&gt;gc.direction_input = brcmexp_gpio_dir_in;</span>
<span class="gi">+	ucb-&gt;gc.direction_output = brcmexp_gpio_dir_out;</span>
<span class="gi">+	ucb-&gt;gc.get_direction = brcmexp_gpio_get_direction;</span>
<span class="gi">+	ucb-&gt;gc.get = brcmexp_gpio_get;</span>
<span class="gi">+	ucb-&gt;gc.set = brcmexp_gpio_set;</span>
<span class="gi">+	ucb-&gt;gc.can_sleep = true;</span>
<span class="gi">+</span>
<span class="gi">+	err = gpiochip_add(&amp;ucb-&gt;gc);</span>
<span class="gi">+	if (err)</span>
<span class="gi">+		return err;</span>
<span class="gi">+</span>
<span class="gi">+	platform_set_drvdata(pdev, ucb);</span>
<span class="gi">+</span>
<span class="gi">+	return 0;</span>
<span class="gi">+}</span>
<span class="gi">+</span>
<span class="gi">+static int brcmexp_gpio_remove(struct platform_device *pdev)</span>
<span class="gi">+{</span>
<span class="gi">+	struct brcmexp_gpio *ucb = platform_get_drvdata(pdev);</span>
<span class="gi">+</span>
<span class="gi">+	gpiochip_remove(&amp;ucb-&gt;gc);</span>
<span class="gi">+</span>
<span class="gi">+	return 0;</span>
<span class="gi">+}</span>
<span class="gi">+</span>
<span class="gi">+static const struct of_device_id __maybe_unused brcmexp_gpio_ids[] = {</span>
<span class="gi">+	{ .compatible = &quot;brcm,bcm2835-expgpio&quot; },</span>
<span class="gi">+	{ }</span>
<span class="gi">+};</span>
<span class="gi">+MODULE_DEVICE_TABLE(of, brcmexp_gpio_ids);</span>
<span class="gi">+</span>
<span class="gi">+static struct platform_driver brcmexp_gpio_driver = {</span>
<span class="gi">+	.driver	= {</span>
<span class="gi">+		.name		= MODULE_NAME,</span>
<span class="gi">+		.owner		= THIS_MODULE,</span>
<span class="gi">+		.of_match_table	= of_match_ptr(brcmexp_gpio_ids),</span>
<span class="gi">+	},</span>
<span class="gi">+	.probe	= brcmexp_gpio_probe,</span>
<span class="gi">+	.remove	= brcmexp_gpio_remove,</span>
<span class="gi">+};</span>
<span class="gi">+module_platform_driver(brcmexp_gpio_driver);</span>
<span class="gi">+</span>
<span class="gi">+MODULE_LICENSE(&quot;GPL&quot;);</span>
<span class="gi">+MODULE_AUTHOR(&quot;Dave Stevenson &lt;dave.stevenson@raspberrypi.org&gt;&quot;);</span>
<span class="gi">+MODULE_DESCRIPTION(&quot;brcm-exp GPIO driver&quot;);</span>
<span class="gi">+MODULE_ALIAS(&quot;platform:brcmexp-gpio&quot;);</span>
<span class="gd">--- a/include/soc/bcm2835/raspberrypi-firmware.h</span>
<span class="gi">+++ b/include/soc/bcm2835/raspberrypi-firmware.h</span>
<span class="gu">@@ -83,7 +83,11 @@ enum rpi_firmware_property_tag {</span>
 	RPI_FIRMWARE_SET_TURBO =                              0x00038009,
 	RPI_FIRMWARE_SET_CUSTOMER_OTP =                       0x00038021,
 	RPI_FIRMWARE_SET_DOMAIN_STATE =                       0x00038030,
<span class="gi">+	RPI_FIRMWARE_GET_GPIO_STATE =                         0x00030041,</span>
<span class="gi">+	RPI_FIRMWARE_SET_GPIO_STATE =                         0x00038041,</span>
 	RPI_FIRMWARE_SET_SDHOST_CLOCK =                       0x00038042,
<span class="gi">+	RPI_FIRMWARE_GET_GPIO_CONFIG =                        0x00030043,</span>
<span class="gi">+	RPI_FIRMWARE_SET_GPIO_CONFIG =                        0x00038043,</span>
 
 	/* Dispmanx TAGS */
 	RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE =                   0x00040001,
</pre></div>
</code></pre></td></tr></table>
</div> <!-- class=content -->
<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit v1.2.3</a> (<a href='https://git-scm.com/'>git 2.25.1</a>) at 2024-07-28 00:17:19 +0000</div>
</div> <!-- id=cgit -->
</body>
</html>
