From: Richard Zhu Date: Tue, 19 Jul 2011 05:42:29 +0000 (+0800) Subject: ENGR00153275-1 ahci L2638 add the standalone ahci temperature monitor X-Git-Tag: v3.0.35-fsl~2229 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=c986649c2901ad7deec342b631301aefff5fdfa6;p=karo-tx-linux.git ENGR00153275-1 ahci L2638 add the standalone ahci temperature monitor based on the 2.6.38 kernel mainline, refer to linux lm-sensors architeture, add the standalone ahci temperature monitor driver on fsl i.mx53 platforms. Less than half sencond is used in one temperature read operation. usage: Use the following cmd to cat the i.mx53 soc temperature after boot up i.mx53 system in user space. for example: ...$ cat /sys/class/hwmon/hwmon1/device/temp1_input 61000 or run the following cmd after configure the lm-sensors ...$ sensors imx-ahci-hwmon-isa-0000 Adapter: ISA adapter temp1: +58.0 C Signed-off-by: Richard Zhu --- diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 10d320782fb4..8589c13f641d 100755 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1374,6 +1374,13 @@ config SENSORS_MC13783_ADC help Support for the A/D converter on MC13783 PMIC. +config SENSORS_IMX_AHCI + tristate "Freescale built-in AHCI temperature monitor" + depends on (ARCH_MX53 && IMX_HAVE_PLATFORM_AHCI) + help + If you say yes here you get support for the built-in AHCI of + the Freescale i.MX53 SoC + if ACPI comment "ACPI drivers" diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index d2291c1ac69f..32bb2da23954 100755 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -122,6 +122,7 @@ obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o obj-$(CONFIG_MXC_MMA8450) += mxc_mma8450.o obj-$(CONFIG_MXC_MMA8451) += mxc_mma8451.o obj-$(CONFIG_SENSORS_DA9052) += da9052-adc.o +obj-$(CONFIG_SENSORS_IMX_AHCI) += imx_ahci_hwmon.o # PMBus drivers obj-$(CONFIG_PMBUS) += pmbus_core.o diff --git a/drivers/hwmon/imx_ahci_hwmon.c b/drivers/hwmon/imx_ahci_hwmon.c new file mode 100644 index 000000000000..073a2d1697dc --- /dev/null +++ b/drivers/hwmon/imx_ahci_hwmon.c @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * struct imx_ahci_hwmon - hwmon information + * @lock: Access lock to serialise the conversions. + * @hwmon_dev: The hwmon device we created. +*/ +struct imx_ahci_hwmon { + struct mutex lock; + struct device *hwmon_dev; +}; + +static ssize_t imx_ahci_hwmon_name_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return sprintf(buf, "imx-ahci-hwmon\n"); +} + +/** + * imx_ahci_hwmon_temp_show - show value of the temperature + * @dev: The device that the attribute belongs to. + * @attr: The attribute being read. + * @buf: The result buffer. + * + * Read a value from the IMX AHCI temperature monitor. + */ +static ssize_t imx_ahci_hwmon_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + void __iomem *mmio; + u32 mpll_test_reg, rtune_ctl_reg, dac_ctl_reg, adc_out_reg; + u32 str1, str2, str3, str4, read_sum, index; + int m1, m2, a, temp, ret; + struct clk *sata_clk, *sata_ref_clk; + struct imx_ahci_hwmon *hwmon; + + hwmon = platform_get_drvdata(to_platform_device(dev)); + + ret = mutex_lock_interruptible(&hwmon->lock); + if (ret < 0) + return ret; + + /* initialize the HW.(kinds of clocks) */ + sata_clk = clk_get(NULL, "imx_sata_clk"); + if (IS_ERR(sata_clk)) { + dev_err(dev, "no sata clock.\n"); + return PTR_ERR(sata_clk); + } + clk_enable(sata_clk); + sata_ref_clk = clk_get(NULL, "usb_phy1_clk"); + if (IS_ERR(sata_ref_clk)) { + dev_err(dev, "no sata clock.\n"); + return PTR_ERR(sata_ref_clk); + } + clk_enable(sata_ref_clk); + + /* map the IO addr */ + mmio = ioremap(MX53_SATA_BASE_ADDR, SZ_2K); + if (mmio == NULL) { + dev_err(dev, "Failed to map SATA REGS\n"); + return -1; + } + + /* check rd-wr to reg */ + read_sum = 0; + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_CRCMP_LT_LIMIT, mmio); + sata_phy_cr_write(read_sum, mmio); + sata_phy_cr_read(&read_sum, mmio); + if ((read_sum & 0xffff) != 0) + dev_err(dev, "Read/Write REG error, 0x%x!\n", read_sum); + + sata_phy_cr_write(0x5A5A, mmio); + sata_phy_cr_read(&read_sum, mmio); + if ((read_sum & 0xffff) != 0x5A5A) + dev_err(dev, "Read/Write REG error, 0x%x!\n", read_sum); + + sata_phy_cr_write(0x1234, mmio); + sata_phy_cr_read(&read_sum, mmio); + if ((read_sum & 0xffff) != 0x1234) + dev_err(dev, "Read/Write REG error, 0x%x!\n", read_sum); + + /* stat temperature test */ + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_MPLL_TST, mmio); + sata_phy_cr_read(&mpll_test_reg, mmio); + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_RTUNE_CTL, mmio); + sata_phy_cr_read(&rtune_ctl_reg, mmio); + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_DAC_CTL, mmio); + sata_phy_cr_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); + + /* Caculate 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; + + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_MPLL_TST, mmio); + sata_phy_cr_write(mpll_test_reg, mmio); + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_DAC_CTL, mmio); + sata_phy_cr_write(dac_ctl_reg, mmio); + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_RTUNE_CTL, mmio); + sata_phy_cr_write(rtune_ctl_reg, mmio); + + /* two dummy read */ + index = 0; + read_sum = 0; + adc_out_reg = 0; + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_ADC_OUT, mmio); + + while (index < 2) { + sata_phy_cr_read(&adc_out_reg, mmio); + /* check if valid */ + if (adc_out_reg & 0x400) + index = index + 1; + read_sum++; + if (read_sum > 100000) { + dev_err(dev, "Read REG more than 100000 times!\n"); + break; + } + } + + index = 0; + read_sum = 0; + while (index < 80) { + sata_phy_cr_read(&adc_out_reg, mmio); + if (adc_out_reg & 0x400) { + read_sum = read_sum + (adc_out_reg & 0x3FF); + index = index + 1; + } + } + /* Use the U32 to make 1000 precision */ + m1 = (read_sum * 1000) / 80; + + /* Caculate the m2 */ + /* rtune_ctl.sel_atbp */ + rtune_ctl_reg = (rtune_ctl_reg & 0xFEF) | (1) << 4; + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_RTUNE_CTL, mmio); + sata_phy_cr_write(rtune_ctl_reg, mmio); + + /* two dummy read */ + index = 0; + read_sum = 0; + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_ADC_OUT, mmio); + while (index < 2) { + sata_phy_cr_read(&adc_out_reg, mmio); + /* check if valid */ + if (adc_out_reg & 0x400) + index = index + 1; + read_sum++; + if (read_sum > 100000) { + dev_err(dev, "Read REG more than 100000 times!\n"); + break; + } + } + + index = 0; + read_sum = 0; + while (index < 80) { + /* FIX ME dead loop protection??? */ + sata_phy_cr_read(&adc_out_reg, mmio); + if (adc_out_reg & 0x400) { + read_sum = read_sum + (adc_out_reg & 0x3FF); + index = index + 1; + } + } + /* 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; + + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_MPLL_TST, mmio); + sata_phy_cr_write(mpll_test_reg, mmio); + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_DAC_CTL, mmio); + sata_phy_cr_write(dac_ctl_reg, mmio); + sata_phy_cr_addr(SATA_PHY_CR_CLOCK_RTUNE_CTL, mmio); + sata_phy_cr_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); + + iounmap(mmio); + + /* Release the clocks */ + clk_disable(sata_ref_clk); + clk_put(sata_ref_clk); + clk_disable(sata_clk); + clk_put(sata_clk); + mutex_unlock(&hwmon->lock); + + return sprintf(buf, "%d\n", temp * 1000); +} + +static DEVICE_ATTR(name, S_IRUGO, imx_ahci_hwmon_name_show, NULL); +static DEVICE_ATTR(temp1_input, S_IRUGO, imx_ahci_hwmon_temp_show, NULL); + +static struct attribute *imx_ahci_hwmon_attrs[] = { + &dev_attr_name.attr, + &dev_attr_temp1_input.attr, + NULL +}; + +static struct attribute_group imx_ahci_hwmon_attrgroup = { + .attrs = imx_ahci_hwmon_attrs, +}; + +/** + * imx_ahci_hwmon_probe - device probe entry. + * @dev: The device being probed. +*/ +static int __devinit imx_ahci_hwmon_probe(struct platform_device *dev) +{ + struct imx_ahci_hwmon *hwmon; + int ret = 0; + + hwmon = kzalloc(sizeof(struct imx_ahci_hwmon), GFP_KERNEL); + if (hwmon == NULL) { + dev_err(&dev->dev, "no memory\n"); + return -ENOMEM; + } + + platform_set_drvdata(dev, hwmon); + + /* add attributes for device. */ + ret = sysfs_create_group(&dev->dev.kobj, &imx_ahci_hwmon_attrgroup); + if (ret) + goto err_mem; + + /* register with the hwmon core */ + + hwmon->hwmon_dev = hwmon_device_register(&dev->dev); + if (IS_ERR(hwmon->hwmon_dev)) { + dev_err(&dev->dev, "error registering with hwmon\n"); + ret = PTR_ERR(hwmon->hwmon_dev); + goto err_raw_attribute; + } + + mutex_init(&hwmon->lock); + + return 0; + +err_raw_attribute: + sysfs_remove_group(&dev->dev.kobj, &imx_ahci_hwmon_attrgroup); + +err_mem: + platform_set_drvdata(dev, NULL); + kfree(hwmon); + return ret; +} + +static int __devexit imx_ahci_hwmon_remove(struct platform_device *dev) +{ + struct imx_ahci_hwmon *hwmon = platform_get_drvdata(dev); + + mutex_destroy(&hwmon->lock); + sysfs_remove_group(&dev->dev.kobj, &imx_ahci_hwmon_attrgroup); + hwmon_device_unregister(hwmon->hwmon_dev); + platform_set_drvdata(dev, NULL); + kfree(hwmon); + + return 0; +} + +static struct platform_driver imx_ahci_hwmon_driver = { + .driver = { + .name = "imx-ahci-hwmon", + .owner = THIS_MODULE, + }, + .probe = imx_ahci_hwmon_probe, + .remove = __devexit_p(imx_ahci_hwmon_remove), +}; + +static int __init imx_ahci_hwmon_init(void) +{ + return platform_driver_register(&imx_ahci_hwmon_driver); +} + +static void __exit imx_ahci_hwmon_exit(void) +{ + platform_driver_unregister(&imx_ahci_hwmon_driver); +} + +module_init(imx_ahci_hwmon_init); +module_exit(imx_ahci_hwmon_exit); + +MODULE_DESCRIPTION("FSL IMX AHCI HWMon driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx-ahci-hwmon");