status = "okay";
};
+ sfctemp: tmon@120e0000 {
+ compatible = "starfive,jh7110-temp";
+ reg = <0x0 0x120e0000 0x0 0x10000>;
+ interrupts = <81>;
+ clocks = <&clkgen JH7110_TEMP_SENSOR_CLK_TEMP>,
+ <&clkgen JH7110_TEMP_SENSOR_CLK_APB>;
+ clock-names = "sense", "bus";
+ resets = <&rstgen RSTN_U0_TEMP_SENSOR_TEMP>,
+ <&rstgen RSTN_U0_TEMP_SENSOR_APB>;
+ reset-names = "sense", "bus";
+ #thermal-sensor-cells = <0>;
+ status = "disabled";
+ };
+
+ thermal-zones {
+ cpu-thermal {
+ polling-delay-passive = <250>;
+ polling-delay = <15000>;
+
+ thermal-sensors = <&sfctemp>;
+
+ cooling-maps {
+ };
+
+ trips {
+ cpu_alert0: cpu_alert0 {
+ /* milliCelsius */
+ temperature = <75000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ cpu_crit: cpu_crit {
+ /* milliCelsius */
+ temperature = <90000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+ };
+
trng: trng@1600C000 {
compatible = "starfive,trng";
reg = <0x0 0x1600C000 0x0 0x4000>;
"tx",
"ptp_ref",
"stmmaceth",
- "pclk";
+ "pclk",
+ "gtxc";
clocks = <&clkgen JH7110_GMAC1_GTXCLK>,
<&clkgen JH7110_GMAC5_CLK_TX>,
<&clkgen JH7110_GMAC5_CLK_PTP>,
<&clkgen JH7110_GMAC5_CLK_AHB>,
- <&clkgen JH7110_GMAC5_CLK_AXI>;
+ <&clkgen JH7110_GMAC5_CLK_AXI>,
+ <&clkgen JH7110_GMAC1_GTXC>;
resets = <&rstgen RSTN_U1_DW_GMAC5_AXI64_H_N>,
<&rstgen RSTN_U1_DW_GMAC5_AXI64_A_I>;
reset-names = "ahb", "stmmaceth";
interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
max-frame-size = <9000>;
phy-mode = "rgmii-id";
- snps,multicast-filter-bins = <256>;
+ snps,multicast-filter-bins = <64>;
snps,perfect-filter-entries = <128>;
- rx-fifo-depth = <262144>;
- tx-fifo-depth = <131072>;
+ rx-fifo-depth = <2048>;
+ tx-fifo-depth = <2048>;
snps,fixed-burst;
snps,no-pbl-x8;
snps,force_thresh_dma_mode;
snps,tso;
snps,en-tx-lpi-clockgating;
snps,en-lpi;
- snps,write-requests = <2>;
- snps,read-requests = <16>;
+ snps,write-requests = <4>;
+ snps,read-requests = <4>;
snps,burst-map = <0x7>;
snps,txpbl = <16>;
snps,rxpbl = <16>;
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
+ * Copyright (C) 2021 Samin Guo <samin.guo@starfivetech.com>
+ */
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+/*
+ * TempSensor reset. The RSTN can be de-asserted once the analog core has
+ * powered up. Trst(min 100ns)
+ * 0:reset 1:de-assert
+ */
+#define SFCTEMP_RSTN BIT(0)
+
+/*
+ * TempSensor analog core power down. The analog core will be powered up
+ * Tpu(min 50us) after PD is de-asserted. RSTN should be held low until the
+ * analog core is powered up.
+ * 0:power up 1:power down
+ */
+#define SFCTEMP_PD BIT(1)
+
+/*
+ * TempSensor start conversion enable.
+ * 0:disable 1:enable
+ */
+#define SFCTEMP_RUN BIT(2)
+
+/*
+ * TempSensor conversion value output.
+ * Temp(C)=DOUT*Y/4094 - K
+ */
+#define SFCTEMP_DOUT_POS 16
+#define SFCTEMP_DOUT_MSK GENMASK(27, 16)
+
+/* DOUT to Celcius conversion constants */
+#define SFCTEMP_Y1000 237500L
+#define SFCTEMP_Z 4094L
+#define SFCTEMP_K1000 81100L
+
+struct sfctemp {
+ /* serialize access to hardware register and enabled below */
+ struct mutex lock;
+ struct completion conversion_done;
+ void __iomem *regs;
+ struct clk *clk_sense;
+ struct clk *clk_bus;
+ struct reset_control *rst_sense;
+ struct reset_control *rst_bus;
+ bool enabled;
+};
+
+static irqreturn_t sfctemp_isr(int irq, void *data)
+{
+ struct sfctemp *sfctemp = data;
+
+ complete(&sfctemp->conversion_done);
+ return IRQ_HANDLED;
+}
+
+static void sfctemp_power_up(struct sfctemp *sfctemp)
+{
+ /* make sure we're powered down first */
+ writel(SFCTEMP_PD, sfctemp->regs);
+ udelay(1);
+
+ writel(0, sfctemp->regs);
+ /* wait t_pu(50us) + t_rst(100ns) */
+ usleep_range(60, 200);
+
+ /* de-assert reset */
+ writel(SFCTEMP_RSTN, sfctemp->regs);
+ udelay(1); /* wait t_su(500ps) */
+}
+
+static void sfctemp_power_down(struct sfctemp *sfctemp)
+{
+ writel(SFCTEMP_PD, sfctemp->regs);
+}
+
+static void sfctemp_run_single(struct sfctemp *sfctemp)
+{
+ writel(SFCTEMP_RSTN | SFCTEMP_RUN, sfctemp->regs);
+ udelay(1);
+ writel(SFCTEMP_RSTN, sfctemp->regs);
+}
+
+static int sfctemp_enable(struct sfctemp *sfctemp)
+{
+ int ret = 0;
+
+ mutex_lock(&sfctemp->lock);
+ if (sfctemp->enabled)
+ goto done;
+
+ ret = clk_prepare_enable(sfctemp->clk_bus);
+ if (ret)
+ goto err;
+ ret = reset_control_deassert(sfctemp->rst_bus);
+ if (ret)
+ goto err_disable_bus;
+
+ ret = clk_prepare_enable(sfctemp->clk_sense);
+ if (ret)
+ goto err_assert_bus;
+ ret = reset_control_deassert(sfctemp->rst_sense);
+ if (ret)
+ goto err_disable_sense;
+
+ sfctemp_power_up(sfctemp);
+ sfctemp->enabled = true;
+done:
+ mutex_unlock(&sfctemp->lock);
+ return ret;
+
+err_disable_sense:
+ clk_disable_unprepare(sfctemp->clk_sense);
+err_assert_bus:
+ reset_control_assert(sfctemp->rst_bus);
+err_disable_bus:
+ clk_disable_unprepare(sfctemp->clk_bus);
+err:
+ mutex_unlock(&sfctemp->lock);
+ return ret;
+}
+
+static int sfctemp_disable(struct sfctemp *sfctemp)
+{
+ mutex_lock(&sfctemp->lock);
+ if (!sfctemp->enabled)
+ goto done;
+
+ sfctemp_power_down(sfctemp);
+ reset_control_assert(sfctemp->rst_sense);
+ clk_disable_unprepare(sfctemp->clk_sense);
+ reset_control_assert(sfctemp->rst_bus);
+ clk_disable_unprepare(sfctemp->clk_bus);
+ sfctemp->enabled = false;
+done:
+ mutex_unlock(&sfctemp->lock);
+ return 0;
+}
+
+static void sfctemp_disable_action(void *data)
+{
+ sfctemp_disable(data);
+}
+
+static int sfctemp_convert(struct sfctemp *sfctemp, long *val)
+{
+ int ret;
+
+ mutex_lock(&sfctemp->lock);
+ if (!sfctemp->enabled) {
+ ret = -ENODATA;
+ goto out;
+ }
+
+ sfctemp_run_single(sfctemp);
+
+ ret = wait_for_completion_interruptible_timeout(&sfctemp->conversion_done,
+ msecs_to_jiffies(10));
+ if (ret <= 0) {
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ /* calculate temperature in milli Celcius */
+ *val = (long)((readl(sfctemp->regs) & SFCTEMP_DOUT_MSK) >> SFCTEMP_DOUT_POS)
+ * SFCTEMP_Y1000 / SFCTEMP_Z - SFCTEMP_K1000;
+
+ ret = 0;
+out:
+ mutex_unlock(&sfctemp->lock);
+ return ret;
+}
+
+static umode_t sfctemp_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_enable:
+ return 0644;
+ case hwmon_temp_input:
+ return 0444;
+ }
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static int sfctemp_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct sfctemp *sfctemp = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_enable:
+ *val = sfctemp->enabled;
+ return 0;
+ case hwmon_temp_input:
+ return sfctemp_convert(sfctemp, val);
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sfctemp_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct sfctemp *sfctemp = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_enable:
+ if (val == 0)
+ return sfctemp_disable(sfctemp);
+ if (val == 1)
+ return sfctemp_enable(sfctemp);
+ break;
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct hwmon_channel_info *sfctemp_info[] = {
+ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops sfctemp_hwmon_ops = {
+ .is_visible = sfctemp_is_visible,
+ .read = sfctemp_read,
+ .write = sfctemp_write,
+};
+
+static const struct hwmon_chip_info sfctemp_chip_info = {
+ .ops = &sfctemp_hwmon_ops,
+ .info = sfctemp_info,
+};
+
+static int sfctemp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *hwmon_dev;
+ struct sfctemp *sfctemp;
+ int ret;
+
+ sfctemp = devm_kzalloc(dev, sizeof(*sfctemp), GFP_KERNEL);
+ if (!sfctemp)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, sfctemp);
+ mutex_init(&sfctemp->lock);
+ init_completion(&sfctemp->conversion_done);
+
+ sfctemp->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(sfctemp->regs))
+ return PTR_ERR(sfctemp->regs);
+
+ sfctemp->clk_sense = devm_clk_get(dev, "sense");
+ if (IS_ERR(sfctemp->clk_sense))
+ return dev_err_probe(dev, PTR_ERR(sfctemp->clk_sense),
+ "error getting sense clock\n");
+
+ sfctemp->clk_bus = devm_clk_get(dev, "bus");
+ if (IS_ERR(sfctemp->clk_bus))
+ return dev_err_probe(dev, PTR_ERR(sfctemp->clk_bus),
+ "error getting bus clock\n");
+
+ sfctemp->rst_sense = devm_reset_control_get_exclusive(dev, "sense");
+ if (IS_ERR(sfctemp->rst_sense))
+ return dev_err_probe(dev, PTR_ERR(sfctemp->rst_sense),
+ "error getting sense reset\n");
+
+ sfctemp->rst_bus = devm_reset_control_get_exclusive(dev, "bus");
+ if (IS_ERR(sfctemp->rst_bus))
+ return dev_err_probe(dev, PTR_ERR(sfctemp->rst_bus),
+ "error getting busreset\n");
+
+ ret = reset_control_assert(sfctemp->rst_sense);
+ if (ret)
+ return dev_err_probe(dev, ret, "error asserting sense reset\n");
+
+ ret = reset_control_assert(sfctemp->rst_bus);
+ if (ret)
+ return dev_err_probe(dev, ret, "error asserting bus reset\n");
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_irq(dev, ret, sfctemp_isr, 0, pdev->name, sfctemp);
+ if (ret)
+ return dev_err_probe(dev, ret, "error requesting irq\n");
+
+ ret = devm_add_action(dev, sfctemp_disable_action, sfctemp);
+ if (ret)
+ return ret;
+
+ ret = sfctemp_enable(sfctemp);
+ if (ret)
+ return dev_err_probe(dev, ret, "error enabling temperature sensor: %d\n", ret);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, pdev->name, sfctemp,
+ &sfctemp_chip_info, NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct of_device_id sfctemp_of_match[] = {
+ { .compatible = "starfive,jh7100-temp" },
+ { .compatible = "starfive,jh7110-temp" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sfctemp_of_match);
+
+static struct platform_driver sfctemp_driver = {
+ .probe = sfctemp_probe,
+ .driver = {
+ .name = "sfctemp",
+ .of_match_table = sfctemp_of_match,
+ },
+};
+module_platform_driver(sfctemp_driver);
+
+MODULE_AUTHOR("Emil Renner Berthing");
+MODULE_DESCRIPTION("StarFive JH7100 temperature sensor driver");
+MODULE_LICENSE("GPL");
static int sf_pwmdac_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
+ int ret = 0;
+ unsigned long mclk_dac_value;
struct sf_pwmdac_dev *dev = dev_get_drvdata(dai->dev);
dev->play_dma_data.addr = dev->mapbase + PWMDAC_WDATA;
+ switch (params_rate(params)) {
+ case 8000:
+ dev->datan = PWMDAC_SAMPLE_CNT_3;
+ mclk_dac_value = 6144000;
+ break;
+ case 16000:
+ dev->datan = PWMDAC_SAMPLE_CNT_3;
+ mclk_dac_value = 12288000;
+ break;
+ case 22050:
+ dev->datan = PWMDAC_SAMPLE_CNT_1;
+ mclk_dac_value = 5644800;
+ break;
+ case 32000:
+ dev->datan = PWMDAC_SAMPLE_CNT_1;
+ mclk_dac_value = 8192000;
+ break;
+ case 44100:
+ dev->datan = PWMDAC_SAMPLE_CNT_1;
+ mclk_dac_value = 11289600;
+ break;
+ case 48000:
+ dev->datan = PWMDAC_SAMPLE_CNT_1;
+ mclk_dac_value = 12288000;
+ break;
+ default:
+ dev_err(dai->dev, "%d rate not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
switch (params_channels(params)) {
case 2:
dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
params_channels(params));
return -EINVAL;
}
-
+
+ /* The mclock for the clock driver always rounds down so add a little slack */
+ mclk_dac_value = mclk_dac_value + 64;
+ pwmdac_set(dev);
+
+ ret = clk_prepare_enable(dev->clk_pwmdac_core);
+ if (ret) {
+ dev_err(dai->dev, "failed to prepare enable clk_pwmdac_core\n");
+ goto err_clk_pwmdac;
+ }
+
+ ret = clk_set_rate(dev->clk_pwmdac_core, mclk_dac_value);
+ if (ret) {
+ dev_err(dai->dev, "failed to set rate for clk_pwmdac_core %lu\n", mclk_dac_value);
+ goto err_clk_pwmdac;
+ }
+
dev->play_dma_data.fifo_size = 1;
dev->play_dma_data.maxburst = 16;
snd_soc_dai_set_drvdata(dai, dev);
return 0;
+
+err_clk_pwmdac:
+ return ret;
+
}
static int sf_pwmdac_clks_get(struct platform_device *pdev,
dev_err(&pdev->dev, "failed to prepare enable clk_pwmdac_core\n");
goto err_clk_pwmdac;
}
+
+ ret = clk_set_rate(dev->clk_pwmdac_core, 4096000);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set rate for clk_pwmdac_core \n");
+ goto err_clk_pwmdac;
+ }
dev_info(&pdev->dev, "clk_apb0 = %lu, clk_pwmdac_apb = %lu, clk_pwmdac_core = %lu\n",
clk_get_rate(dev->clk_apb0), clk_get_rate(dev->clk_pwmdac_apb),
clk_get_rate(dev->clk_pwmdac_core));
.playback = {
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_16000,
+ .rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &sf_pwmdac_dai_ops,