Skip to content
Permalink
Browse files
accept an external clock for the time
  • Loading branch information
ddrown committed Apr 27, 2020
1 parent 6e9d15c commit 399e6430fcd49fa2eaaf21eafbfa86ec79f99579
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 21 deletions.
@@ -1,5 +1,9 @@
* devicetree.patch - contains the device tree changes to have a PPS on PA8 (tim1 ch1), and uses tim5 as a kernel clocksource
* devicetree.patch - contains the device tree changes to have a PPS on PA8 (tim1 ch1), and uses tim5 as a kernel clocksource using a 24MHz signal on PH11
* clocksource-stm32 - provides a kernel clocksource (provides the wall clock time)
* pps-stm32 - hardware pps timestamp capture driver

both drivers are built on top of the stm32-timers mfd driver

The clocksource driver accepts arguments from device tree:
* clock-source = <2>; // use channel 2, clock-source=0 means use 209MHz internal clock
* clock-frequency = <24000000>; // needed for external channels
@@ -1,4 +1,5 @@
#include <linux/mfd/stm32-timers.h>
#include <linux/pinctrl/consumer.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
@@ -7,12 +8,43 @@
#include <linux/bits.h>
#include <linux/clocksource.h>

#define TIM_CR1_UDIS BIT(1) /* UEV disabled */
#define TIM_CR1_UDIS BIT(1) /* UEV disabled */
#define TIM_SMCR_TS_TI1 BIT(6)|BIT(4)
#define TIM_SMCR_TS_TI2 BIT(6)|BIT(5)
#define TIM_SMCR_TS_ETRF BIT(6)|BIT(5)|BIT(4)
#define TIM_SMCR_ECE BIT(14) // external clock mode 2
#define TIM_SMCR_SMS_EM1 BIT(2)|BIT(1)|BIT(0)

struct stm32_clocksource {
struct regmap *regmap;
struct clk *clk;
struct clocksource clksrc;
u32 rate;
int channel;
};

static ssize_t sysfs_show(struct device *dev, int register_offset, char *buf) {
u32 register_value = 0;

struct stm32_clocksource *ddata = dev_get_drvdata(dev);
regmap_read(ddata->regmap, register_offset, &register_value);

return snprintf(buf, PAGE_SIZE, "%u\n", register_value);
}

static ssize_t cnt_show(struct device *dev, struct device_attribute *attr, char *buf) {
return sysfs_show(dev, TIM_CNT, buf);
}

static DEVICE_ATTR(cnt, S_IRUGO, cnt_show, NULL);

static struct attribute *attrs[] = {
&dev_attr_cnt.attr,
NULL,
};

static struct attribute_group attr_group = {
.attrs = attrs,
};

static inline struct stm32_clocksource *from_clocksource(struct clocksource *c)
@@ -29,18 +61,67 @@ static u64 stm32_clocksource_read(struct clocksource *cs) {
}

static void stm32_clocksource_init(struct platform_device *pdev) {
u32 rate;
struct stm32_clocksource *ddata = dev_get_drvdata(&pdev->dev);

rate = clk_get_rate(ddata->clk);

ddata->clksrc.name = pdev->name;
ddata->clksrc.rating = 250;
ddata->clksrc.read = stm32_clocksource_read;
ddata->clksrc.mask = CLOCKSOURCE_MASK(32);
ddata->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;

clocksource_register_hz(&ddata->clksrc, rate);
clocksource_register_hz(&ddata->clksrc, ddata->rate);
}

static int stm32_clocksource_get_channel(struct platform_device *pdev) {
const __be32 *val;
struct pinctrl *pinctrl;
u32 channel;

pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
dev_warn(&pdev->dev, "failed to setup gpio pins\n");
return 0;
}

val = of_get_property(pdev->dev.of_node, "clock-source", NULL);
if(!val) {
dev_info(&pdev->dev, "using internal clock\n");
return 0;
}

channel = be32_to_cpup(val);
if(channel == 1 || channel == 2)
return channel;

dev_warn(&pdev->dev, "unsupported channel %u, using internal clock\n", channel);
return 0;
}

static int stm32_clocksource_set_channel(struct platform_device *pdev, struct stm32_clocksource *ddata) {
const __be32 *val;
u32 rate;

ddata->rate = clk_get_rate(ddata->clk);
regmap_update_bits(ddata->regmap, TIM_SMCR, TIM_SMCR_SMS|TIM_SMCR_ECE, 0); // switch to internal clock

if(ddata->channel == 0) {
return 0;
}

val = of_get_property(pdev->dev.of_node, "clock-frequency", NULL);
if(!val) {
dev_warn(&pdev->dev, "no clock frequency set, using internal clock");
return 0;
}

rate = be32_to_cpup(val);
ddata->rate = rate;

regmap_update_bits(ddata->regmap, TIM_SMCR, TIM_SMCR_TS, (ddata->channel == 1) ? TIM_SMCR_TS_TI1 : TIM_SMCR_TS_TI2);
regmap_update_bits(ddata->regmap, TIM_SMCR, TIM_SMCR_SMS, TIM_SMCR_SMS_EM1);
dev_info(&pdev->dev, "using channel %u, rate %u\n", ddata->channel, ddata->rate);

return 0;
}

static int stm32_clocksource_probe(struct platform_device *pdev) {
@@ -64,12 +145,19 @@ static int stm32_clocksource_probe(struct platform_device *pdev) {

platform_set_drvdata(pdev, priv);

priv->channel = stm32_clocksource_get_channel(pdev);
stm32_clocksource_set_channel(pdev, priv);

// start counter
regmap_write(priv->regmap, TIM_ARR, UINT_MAX);
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN|TIM_CR1_UDIS, TIM_CR1_CEN|TIM_CR1_UDIS);

stm32_clocksource_init(pdev);

if(sysfs_create_group(&dev->kobj, &attr_group)) {
dev_err(dev, "sysfs_create_group failed\n");
}

return 0;
}

@@ -78,6 +166,8 @@ static int stm32_clocksource_remove(struct platform_device *pdev) {

clocksource_unregister(&ddata->clksrc);

sysfs_remove_group(&pdev->dev.kobj, &attr_group);

// stop counter
regmap_update_bits(ddata->regmap, TIM_CR1, TIM_CR1_CEN, 0);

@@ -1,15 +1,21 @@
diff --git a/arch/arm/boot/dts/stm32mp157a-dk1.dts b/arch/arm/boot/dts/stm32mp157a-dk1.dts
index 9ec45de3c664..26ae438d2091 100644
index 9ec45de3c664..df67b59455a1 100644
--- a/arch/arm/boot/dts/stm32mp157a-dk1.dts
+++ b/arch/arm/boot/dts/stm32mp157a-dk1.dts
@@ -617,20 +626,34 @@
@@ -617,20 +617,40 @@
status = "disabled";
};

+&pinctrl {
+ pps_pins: pps-pins-0 {
+ pins {
+ pinmux = <STM32_PINMUX('A', 8, AF1)>; /* TIM1_CH1 */
+ pinmux = <STM32_PINMUX('A', 8, AF1)>; /* TIM1_CH1, PA8, pin 7 on rpi header */
+ bias-disable;
+ };
+ };
+ tcxo: tim5-ch2 {
+ pins {
+ pinmux = <STM32_PINMUX('H', 11, AF2)>; /* TIM5_CH2, PH11, pin 31 on rpi header */
+ bias-disable;
+ };
+ };
@@ -50,25 +56,29 @@ index 9ec45de3c664..26ae438d2091 100644
};

&timers3 {
@@ -666,7 +689,8 @@
@@ -666,15 +686,17 @@
&timers5 {
/delete-property/dmas;
/delete-property/dma-names;
- status = "disabled";
- pwm {
- pinctrl-0 = <&pwm5_pins_a>;
- pinctrl-1 = <&pwm5_sleep_pins_a>;
- pinctrl-names = "default", "sleep";
- status = "okay";
- };
- timer@4 {
+ /delete-property/pwm;
+ /delete-property/timer@4;
+ status = "okay";
+
pwm {
pinctrl-0 = <&pwm5_pins_a>;
pinctrl-1 = <&pwm5_sleep_pins_a>;
@@ -676,6 +700,11 @@
timer@4 {
status = "okay";
};
+
+ clocksource@5 {
+ compatible = "st,stm32-timer-clocksource";
+ status = "okay";
+ };
status = "okay";
+ pinctrl-0 = <&tcxo>;
+ pinctrl-names = "default";
+ clock-source = <2>;
+ clock-frequency = <24000000>;
};
};

&timers6 {

0 comments on commit 399e643

Please sign in to comment.