From: Csaba Kertesz <[email protected]>

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 <[email protected]>
---
 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

Reply via email to