Skip to content

Commit

Permalink
nfc: s3fwrn5: i2c: Enable optional clock from device tree
Browse files Browse the repository at this point in the history
S3FWRN5 depends on a clock input ("XI" pin) to function properly.
Depending on the hardware configuration this could be an always-on
oscillator or some external clock that must be explicitly enabled.

So far we assumed that the clock is always-on.
Make the driver request an (optional) clock from the device tree
and make sure the clock is running before starting S3FWRN5.

Note: S3FWRN5 asserts "GPIO2" whenever it needs the clock input to
function correctly. On some hardware configurations, GPIO2 is
connected directly to an input pin of the external clock provider
(e.g. the main PMIC of the SoC). In that case, it can automatically
AND the clock enable bit and clock request from S3FWRN5 so that
the clock is actually only enabled when needed.

It is also conceivable that on some other hardware configuration
S3FWRN5's GPIO2 might be connected as a regular GPIO input
of the SoC. In that case, follow-up patches could extend the
driver to request the GPIO, set up an interrupt and only enable
the clock when requested by S3FWRN5.

Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Stephan Gerhold authored and David S. Miller committed May 19, 2021
1 parent 9cc52f5 commit 340f42f
Showing 1 changed file with 28 additions and 2 deletions.
30 changes: 28 additions & 2 deletions drivers/nfc/s3fwrn5/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Robert Baldyga <r.baldyga@samsung.com>
*/

#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/delay.h>
Expand All @@ -22,6 +23,7 @@
struct s3fwrn5_i2c_phy {
struct phy_common common;
struct i2c_client *i2c_dev;
struct clk *clk;

unsigned int irq_skip:1;
};
Expand Down Expand Up @@ -207,17 +209,40 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client,
if (ret < 0)
return ret;

phy->clk = devm_clk_get_optional(&client->dev, NULL);
if (IS_ERR(phy->clk))
return dev_err_probe(&client->dev, PTR_ERR(phy->clk),
"failed to get clock\n");

/*
* S3FWRN5 depends on a clock input ("XI" pin) to function properly.
* Depending on the hardware configuration this could be an always-on
* oscillator or some external clock that must be explicitly enabled.
* Make sure the clock is running before starting S3FWRN5.
*/
ret = clk_prepare_enable(phy->clk);
if (ret < 0) {
dev_err(&client->dev, "failed to enable clock: %d\n", ret);
return ret;
}

ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->i2c_dev->dev,
&i2c_phy_ops);
if (ret < 0)
return ret;
goto disable_clk;

ret = devm_request_threaded_irq(&client->dev, phy->i2c_dev->irq, NULL,
s3fwrn5_i2c_irq_thread_fn, IRQF_ONESHOT,
S3FWRN5_I2C_DRIVER_NAME, phy);
if (ret)
s3fwrn5_remove(phy->common.ndev);
goto s3fwrn5_remove;

return 0;

s3fwrn5_remove:
s3fwrn5_remove(phy->common.ndev);
disable_clk:
clk_disable_unprepare(phy->clk);
return ret;
}

Expand All @@ -226,6 +251,7 @@ static int s3fwrn5_i2c_remove(struct i2c_client *client)
struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client);

s3fwrn5_remove(phy->common.ndev);
clk_disable_unprepare(phy->clk);

return 0;
}
Expand Down

0 comments on commit 340f42f

Please sign in to comment.