From: Csaba Kertesz <csaba.kertesz@xxxxxxxxx> The original patch was made by Richard Zhu for kernel 2.6.x: ENGR00134041-MX53-Add-the-SATA-AHCI-temperature-monitor.patch The old source code was migrated to the new kernel 3.x. The concept of value reading was changed a bit: 1. The new 3.x kernel functions (imx_phy_reg_read, imx_phy_reg_write) use 16 bit registers while the original implementation used 32 bit integers for this purpose. 2. The communication is guarded against infinite loop to give up a certain register reading after 100000 attempts. This number comes from the original implementation. A new variable (read_attempt) is introduced to count the trials. Signed-off-by: Fabien Lahoudere <fabien.lahoudere@xxxxxxxxxxxxxxx> --- drivers/ata/ahci_imx.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 202 insertions(+), 1 deletion(-) diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index 3f3a7db..b89eb45 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -214,6 +214,194 @@ static int imx_sata_phy_reset(struct ahci_host_priv *hpriv) return timeout ? 0 : -ETIMEDOUT; } +enum { + /* SATA PHY Register */ + SATA_PHY_CR_CLOCK_CRCMP_LT_LIMIT = 0x0001, + SATA_PHY_CR_CLOCK_DAC_CTL = 0x0008, + SATA_PHY_CR_CLOCK_RTUNE_CTL = 0x0009, + SATA_PHY_CR_CLOCK_ADC_OUT = 0x000A, + SATA_PHY_CR_CLOCK_MPLL_TST = 0x0017, +}; + +/* SATA AHCI temperature monitor */ +static ssize_t sata_ahci_current_tmp(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + u16 mpll_test_reg, rtune_ctl_reg, dac_ctl_reg, adc_out_reg, read_sum; + u32 str1, str2, str3, str4, index, read_attempt; + const u32 attempt_limit = 100000; + int m1, m2, a, temp; + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + + /* check rd-wr to reg */ + read_sum = 0; + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_CRCMP_LT_LIMIT, mmio); + imx_phy_reg_write(read_sum, mmio); + imx_phy_reg_read(&read_sum, mmio); + if ((read_sum & 0xffff) != 0) + dev_err(dev, "Read/Write REG error, 0x%x!\n", read_sum); + + imx_phy_reg_write(0x5A5A, mmio); + imx_phy_reg_read(&read_sum, mmio); + if ((read_sum & 0xffff) != 0x5A5A) + dev_err(dev, "Read/Write REG error, 0x%x!\n", read_sum); + + imx_phy_reg_write(0x1234, mmio); + imx_phy_reg_read(&read_sum, mmio); + if ((read_sum & 0xffff) != 0x1234) + dev_err(dev, "Read/Write REG error, 0x%x!\n", read_sum); + + /* start temperature test */ + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_MPLL_TST, mmio); + imx_phy_reg_read(&mpll_test_reg, mmio); + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_RTUNE_CTL, mmio); + imx_phy_reg_read(&rtune_ctl_reg, mmio); + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_DAC_CTL, mmio); + imx_phy_reg_read(&dac_ctl_reg, mmio); + + /* mpll_tst.meas_iv ([12:2]) */ + str1 = (mpll_test_reg >> 2) & 0x7FF; + /* rtune_ctl.mode ([1:0]) */ + str2 = (rtune_ctl_reg) & 0x3; + /* dac_ctl.dac_mode ([14:12]) */ + str3 = (dac_ctl_reg >> 12) & 0x7; + /* rtune_ctl.sel_atbp ([4]) */ + str4 = (rtune_ctl_reg >> 4); + + /* Calculate the m1 */ + /* mpll_tst.meas_iv */ + mpll_test_reg = (mpll_test_reg & 0xE03) | (512) << 2; + /* rtune_ctl.mode */ + rtune_ctl_reg = (rtune_ctl_reg & 0xFFC) | (1); + /* dac_ctl.dac_mode */ + dac_ctl_reg = (dac_ctl_reg & 0x8FF) | (4) << 12; + /* rtune_ctl.sel_atbp */ + rtune_ctl_reg = (rtune_ctl_reg & 0xFEF) | (0) << 4; + + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_MPLL_TST, mmio); + imx_phy_reg_write(mpll_test_reg, mmio); + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_DAC_CTL, mmio); + imx_phy_reg_write(dac_ctl_reg, mmio); + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_RTUNE_CTL, mmio); + imx_phy_reg_write(rtune_ctl_reg, mmio); + + /* two dummy read */ + index = 0; + read_attempt = 0; + adc_out_reg = 0; + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_ADC_OUT, mmio); + while (index < 2) { + imx_phy_reg_read(&adc_out_reg, mmio); + /* check if valid */ + if (adc_out_reg & 0x400) + index++; + + read_attempt++; + if (read_attempt > attempt_limit) { + dev_err(dev, "Read REG more than 100000 times!\n"); + break; + } + } + + index = 0; + read_attempt = 0; + read_sum = 0; + while (index < 80) { + imx_phy_reg_read(&adc_out_reg, mmio); + if (adc_out_reg & 0x400) { + read_sum = read_sum + (adc_out_reg & 0x3FF); + index++; + } + read_attempt++; + if (read_attempt > attempt_limit) { + dev_err(dev, "Read REG more than 100000 times!\n"); + break; + } + } + /* Use the U32 to make 1000 precision */ + m1 = (read_sum * 1000) / 80; + + /* Calculate the m2 */ + /* rtune_ctl.sel_atbp */ + rtune_ctl_reg = (rtune_ctl_reg & 0xFEF) | (1) << 4; + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_RTUNE_CTL, mmio); + imx_phy_reg_write(rtune_ctl_reg, mmio); + + /* two dummy read */ + index = 0; + read_attempt = 0; + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_ADC_OUT, mmio); + while (index < 2) { + imx_phy_reg_read(&adc_out_reg, mmio); + /* check if valid */ + if (adc_out_reg & 0x400) + index++; + + read_attempt++; + if (read_attempt > attempt_limit) { + dev_err(dev, "Read REG more than 100000 times!\n"); + break; + } + } + + index = 0; + read_attempt = 0; + read_sum = 0; + while (index < 80) { + imx_phy_reg_read(&adc_out_reg, mmio); + if (adc_out_reg & 0x400) { + read_sum = read_sum + (adc_out_reg & 0x3FF); + index++; + } + read_attempt++; + if (read_attempt > attempt_limit) { + dev_err(dev, "Read REG more than 100000 times!\n"); + break; + } + } + /* Use the U32 to make 1000 precision */ + m2 = (read_sum * 1000) / 80; + + /* restore the status */ + /* mpll_tst.meas_iv */ + mpll_test_reg = (mpll_test_reg & 0xE03) | (str1) << 2; + /* rtune_ctl.mode */ + rtune_ctl_reg = (rtune_ctl_reg & 0xFFC) | (str2); + /* dac_ctl.dac_mode */ + dac_ctl_reg = (dac_ctl_reg & 0x8FF) | (str3) << 12; + /* rtune_ctl.sel_atbp */ + rtune_ctl_reg = (rtune_ctl_reg & 0xFEF) | (str4) << 4; + + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_MPLL_TST, mmio); + imx_phy_reg_write(mpll_test_reg, mmio); + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_DAC_CTL, mmio); + imx_phy_reg_write(dac_ctl_reg, mmio); + imx_phy_reg_addressing(SATA_PHY_CR_CLOCK_RTUNE_CTL, mmio); + imx_phy_reg_write(rtune_ctl_reg, mmio); + + /* Compute temperature */ + if (!(m2 / 1000)) + m2 = 1000; + a = (m2 - m1) / (m2 / 1000); + temp = ((((-559) * a) / 1000) * a) / 1000 + (1379) * a / 1000 + (-458); + + return sprintf(buf, "%d\n", temp); +} + + +static DEVICE_ATTR(temperature, S_IRUGO, sata_ahci_current_tmp, NULL); + +static struct attribute *fsl_sata_ahci_attr[] = { + &dev_attr_temperature.attr, + NULL +}; + +static const struct attribute_group fsl_sata_ahci_group = { + .attrs = fsl_sata_ahci_attr, +}; + static int imx_sata_enable(struct ahci_host_priv *hpriv) { struct imx_ahci_priv *imxpriv = hpriv->plat_data; @@ -597,9 +785,16 @@ static int imx_ahci_probe(struct platform_device *pdev) if (ret) return ret; + if (imxpriv->type == AHCI_IMX53) { + /* Add the temperature monitor */ + ret = sysfs_create_group(&dev->kobj, &fsl_sata_ahci_group); + if (ret) + goto disable_clk; + } + ret = imx_sata_enable(hpriv); if (ret) - goto disable_clk; + goto disable_sysfs; /* * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, @@ -631,6 +826,9 @@ static int imx_ahci_probe(struct platform_device *pdev) disable_sata: imx_sata_disable(hpriv); +disable_sysfs: + if (imxpriv->type == AHCI_IMX53) + sysfs_remove_group(&dev->kobj, &fsl_sata_ahci_group); disable_clk: clk_disable_unprepare(imxpriv->sata_clk); return ret; @@ -640,8 +838,11 @@ static void ahci_imx_host_stop(struct ata_host *host) { struct ahci_host_priv *hpriv = host->private_data; struct imx_ahci_priv *imxpriv = hpriv->plat_data; + struct device *dev = &imxpriv->ahci_pdev->dev; imx_sata_disable(hpriv); + if (imxpriv->type == AHCI_IMX53) + sysfs_remove_group(&dev->kobj, &fsl_sata_ahci_group); clk_disable_unprepare(imxpriv->sata_clk); } -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html