@@ -9,6 +9,8 @@
* Copyright (c) 2013 Semtech-Cycleo
*/
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -42,10 +44,16 @@ static const struct reg_field sx125x_regmap_fields[] = {
};
struct sx125x_priv {
+ struct clk *clkout;
+ struct clk_hw clkout_hw;
+
+ struct device *dev;
struct regmap *regmap;
struct regmap_field *regmap_fields[ARRAY_SIZE(sx125x_regmap_fields)];
};
+#define to_clkout(_hw) container_of(_hw, struct sx125x_priv, clkout_hw)
+
static struct regmap_config __maybe_unused sx125x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -64,6 +72,96 @@ static int sx125x_field_write(struct sx125x_priv *priv,
return regmap_field_write(priv->regmap_fields[field_id], val);
}
+static int sx125x_field_read(struct sx125x_priv *priv,
+ enum sx125x_fields field_id, unsigned int *val)
+{
+ return regmap_field_read(priv->regmap_fields[field_id], val);
+}
+
+static int sx125x_clkout_enable(struct clk_hw *hw)
+{
+ struct sx125x_priv *priv = to_clkout(hw);
+
+ dev_info(priv->dev, "enabling clkout\n");
+ return sx125x_field_write(priv, F_CLK_OUT, 1);
+}
+
+static void sx125x_clkout_disable(struct clk_hw *hw)
+{
+ struct sx125x_priv *priv = to_clkout(hw);
+ int ret;
+
+ dev_info(priv->dev, "disabling clkout\n");
+ ret = sx125x_field_write(priv, F_CLK_OUT, 0);
+ if (ret)
+ dev_err(priv->dev, "error disabling clkout\n");
+}
+
+static int sx125x_clkout_is_enabled(struct clk_hw *hw)
+{
+ struct sx125x_priv *priv = to_clkout(hw);
+ unsigned int enabled;
+ int ret;
+
+ ret = sx125x_field_read(priv, F_CLK_OUT, &enabled);
+ if (ret) {
+ dev_err(priv->dev, "error reading clk enable\n");
+ return 0;
+ }
+ return enabled;
+}
+
+static const struct clk_ops sx125x_clkout_ops = {
+ .enable = sx125x_clkout_enable,
+ .disable = sx125x_clkout_disable,
+ .is_enabled = sx125x_clkout_is_enabled,
+};
+
+static int sx125x_register_clock_provider(struct sx125x_priv *priv)
+{
+ struct device *dev = priv->dev;
+ struct clk_init_data init;
+ const char *parent;
+ int ret;
+
+ /* Disable CLKOUT */
+ ret = sx125x_field_write(priv, F_CLK_OUT, 0);
+ if (ret) {
+ dev_err(dev, "unable to disable clkout\n");
+ return ret;
+ }
+
+ /* Register clock provider if expected in DTB */
+ if (!of_find_property(dev->of_node, "#clock-cells", NULL))
+ return 0;
+
+ dev_info(dev, "registering clkout\n");
+
+ parent = of_clk_get_parent_name(dev->of_node, 0);
+ if (!parent) {
+ dev_err(dev, "Unable to find parent clk\n");
+ return -ENODEV;
+ }
+
+ init.ops = &sx125x_clkout_ops;
+ init.flags = CLK_IS_BASIC;
+ init.parent_names = &parent;
+ init.num_parents = 1;
+ priv->clkout_hw.init = &init;
+
+ of_property_read_string_index(dev->of_node, "clock-output-names", 0,
+ &init.name);
+
+ priv->clkout = devm_clk_register(dev, &priv->clkout_hw);
+ if (IS_ERR(priv->clkout)) {
+ dev_err(dev, "failed to register clkout\n");
+ return PTR_ERR(priv->clkout);
+ }
+ ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get,
+ &priv->clkout_hw);
+ return ret;
+}
+
static int __maybe_unused sx125x_regmap_probe(struct device *dev, struct regmap *regmap, unsigned int radio)
{
struct sx125x_priv *priv;
@@ -76,6 +174,7 @@ static int __maybe_unused sx125x_regmap_probe(struct device *dev, struct regmap
return -ENOMEM;
dev_set_drvdata(dev, priv);
+ priv->dev = dev;
priv->regmap = regmap;
for (i = 0; i < ARRAY_SIZE(sx125x_regmap_fields); i++) {
const struct reg_field *reg_fields = sx125x_regmap_fields;
@@ -99,16 +198,13 @@ static int __maybe_unused sx125x_regmap_probe(struct device *dev, struct regmap
dev_info(dev, "SX125x version: %02x\n", val);
}
- if (radio == 1) { /* HACK */
- ret = sx125x_field_write(priv, F_CLK_OUT, 1);
- if (ret) {
- dev_err(dev, "enabling clock output failed\n");
- return ret;
- }
-
- dev_info(dev, "enabling clock output\n");
+ ret = sx125x_register_clock_provider(priv);
+ if (ret) {
+ dev_err(dev, "failed to register clkout provider: %d\n", ret);
+ return ret;
}
+ /* TODO Only needs setting on radio on the TX path */
ret = sx125x_field_write(priv, F_TX_DAC_CLK_SEL, 1);
if (ret) {
dev_err(dev, "clock select failed\n");
@@ -10,6 +10,7 @@
*/
#include <linux/bitops.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/lora.h>
@@ -395,6 +396,18 @@ static int sx130x_loradev_open(struct net_device *netdev)
return -ENXIO;
}
+ priv->clk32m = devm_clk_get(priv->dev, "clk32m");
+ if (IS_ERR(priv->clk32m)) {
+ dev_err(priv->dev, "failed to get clk32m\n");
+ return PTR_ERR(priv->clk32m);
+ }
+
+ ret = clk_prepare_enable(priv->clk32m);
+ if (ret) {
+ dev_err(priv->dev, "failed to enable clk32m: %d\n", ret);
+ return ret;
+ }
+
ret = sx1301_field_write(priv, F_GLOBAL_EN, 1);
if (ret) {
dev_err(priv->dev, "enable global clocks failed\n");
@@ -9,6 +9,7 @@
#ifndef _SX1301_
#define _SX1301_
+#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>
#include <linux/lora/dev.h>
@@ -109,6 +110,7 @@ static const struct reg_field sx1301_regmap_fields[] = {
struct sx1301_priv {
struct lora_dev_priv lora;
struct device *dev;
+ struct clk *clk32m;
struct spi_device *spi;
struct gpio_desc *rst_gpio;
struct regmap *regmap;