12dfef650SFabio Estevam // SPDX-License-Identifier: GPL-2.0 22dfef650SFabio Estevam // 32dfef650SFabio Estevam // Copyright 2016 Freescale Semiconductor, Inc. 443528445SJia Hongtao 551904045SAnson Huang #include <linux/clk.h> 643528445SJia Hongtao #include <linux/module.h> 743528445SJia Hongtao #include <linux/platform_device.h> 843528445SJia Hongtao #include <linux/err.h> 943528445SJia Hongtao #include <linux/io.h> 1043528445SJia Hongtao #include <linux/of.h> 1143528445SJia Hongtao #include <linux/of_address.h> 1243528445SJia Hongtao #include <linux/thermal.h> 1343528445SJia Hongtao 1443528445SJia Hongtao #include "thermal_core.h" 1543528445SJia Hongtao 1643528445SJia Hongtao #define SITES_MAX 16 1743528445SJia Hongtao 1843528445SJia Hongtao /* 1943528445SJia Hongtao * QorIQ TMU Registers 2043528445SJia Hongtao */ 2143528445SJia Hongtao struct qoriq_tmu_site_regs { 2243528445SJia Hongtao u32 tritsr; /* Immediate Temperature Site Register */ 2343528445SJia Hongtao u32 tratsr; /* Average Temperature Site Register */ 2443528445SJia Hongtao u8 res0[0x8]; 2543528445SJia Hongtao }; 2643528445SJia Hongtao 2743528445SJia Hongtao struct qoriq_tmu_regs { 2843528445SJia Hongtao u32 tmr; /* Mode Register */ 2943528445SJia Hongtao #define TMR_DISABLE 0x0 3043528445SJia Hongtao #define TMR_ME 0x80000000 3143528445SJia Hongtao #define TMR_ALPF 0x0c000000 3243528445SJia Hongtao u32 tsr; /* Status Register */ 3343528445SJia Hongtao u32 tmtmir; /* Temperature measurement interval Register */ 3443528445SJia Hongtao #define TMTMIR_DEFAULT 0x0000000f 3543528445SJia Hongtao u8 res0[0x14]; 3643528445SJia Hongtao u32 tier; /* Interrupt Enable Register */ 3743528445SJia Hongtao #define TIER_DISABLE 0x0 3843528445SJia Hongtao u32 tidr; /* Interrupt Detect Register */ 3943528445SJia Hongtao u32 tiscr; /* Interrupt Site Capture Register */ 4043528445SJia Hongtao u32 ticscr; /* Interrupt Critical Site Capture Register */ 4143528445SJia Hongtao u8 res1[0x10]; 4243528445SJia Hongtao u32 tmhtcrh; /* High Temperature Capture Register */ 4343528445SJia Hongtao u32 tmhtcrl; /* Low Temperature Capture Register */ 4443528445SJia Hongtao u8 res2[0x8]; 4543528445SJia Hongtao u32 tmhtitr; /* High Temperature Immediate Threshold */ 4643528445SJia Hongtao u32 tmhtatr; /* High Temperature Average Threshold */ 4743528445SJia Hongtao u32 tmhtactr; /* High Temperature Average Crit Threshold */ 4843528445SJia Hongtao u8 res3[0x24]; 4943528445SJia Hongtao u32 ttcfgr; /* Temperature Configuration Register */ 5043528445SJia Hongtao u32 tscfgr; /* Sensor Configuration Register */ 5143528445SJia Hongtao u8 res4[0x78]; 5243528445SJia Hongtao struct qoriq_tmu_site_regs site[SITES_MAX]; 5343528445SJia Hongtao u8 res5[0x9f8]; 5443528445SJia Hongtao u32 ipbrr0; /* IP Block Revision Register 0 */ 5543528445SJia Hongtao u32 ipbrr1; /* IP Block Revision Register 1 */ 5643528445SJia Hongtao u8 res6[0x310]; 5743528445SJia Hongtao u32 ttr0cr; /* Temperature Range 0 Control Register */ 5843528445SJia Hongtao u32 ttr1cr; /* Temperature Range 1 Control Register */ 5943528445SJia Hongtao u32 ttr2cr; /* Temperature Range 2 Control Register */ 6043528445SJia Hongtao u32 ttr3cr; /* Temperature Range 3 Control Register */ 6143528445SJia Hongtao }; 6243528445SJia Hongtao 637797ff42SYuantian Tang struct qoriq_tmu_data; 647797ff42SYuantian Tang 6543528445SJia Hongtao /* 6643528445SJia Hongtao * Thermal zone data 6743528445SJia Hongtao */ 687797ff42SYuantian Tang struct qoriq_sensor { 697797ff42SYuantian Tang struct thermal_zone_device *tzd; 707797ff42SYuantian Tang struct qoriq_tmu_data *qdata; 717797ff42SYuantian Tang int id; 727797ff42SYuantian Tang }; 737797ff42SYuantian Tang 7443528445SJia Hongtao struct qoriq_tmu_data { 7543528445SJia Hongtao struct qoriq_tmu_regs __iomem *regs; 7651904045SAnson Huang struct clk *clk; 7743528445SJia Hongtao bool little_endian; 787797ff42SYuantian Tang struct qoriq_sensor *sensor[SITES_MAX]; 7943528445SJia Hongtao }; 8043528445SJia Hongtao 8143528445SJia Hongtao static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr) 8243528445SJia Hongtao { 8343528445SJia Hongtao if (p->little_endian) 8443528445SJia Hongtao iowrite32(val, addr); 8543528445SJia Hongtao else 8643528445SJia Hongtao iowrite32be(val, addr); 8743528445SJia Hongtao } 8843528445SJia Hongtao 8943528445SJia Hongtao static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) 9043528445SJia Hongtao { 9143528445SJia Hongtao if (p->little_endian) 9243528445SJia Hongtao return ioread32(addr); 9343528445SJia Hongtao else 9443528445SJia Hongtao return ioread32be(addr); 9543528445SJia Hongtao } 9643528445SJia Hongtao 9743528445SJia Hongtao static int tmu_get_temp(void *p, int *temp) 9843528445SJia Hongtao { 997797ff42SYuantian Tang struct qoriq_sensor *qsensor = p; 1007797ff42SYuantian Tang struct qoriq_tmu_data *qdata = qsensor->qdata; 10143528445SJia Hongtao u32 val; 10243528445SJia Hongtao 1037797ff42SYuantian Tang val = tmu_read(qdata, &qdata->regs->site[qsensor->id].tritsr); 10443528445SJia Hongtao *temp = (val & 0xff) * 1000; 10543528445SJia Hongtao 10643528445SJia Hongtao return 0; 10743528445SJia Hongtao } 10843528445SJia Hongtao 1097797ff42SYuantian Tang static const struct thermal_zone_of_device_ops tmu_tz_ops = { 1107797ff42SYuantian Tang .get_temp = tmu_get_temp, 1117797ff42SYuantian Tang }; 1127797ff42SYuantian Tang 1137797ff42SYuantian Tang static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev) 11443528445SJia Hongtao { 1157797ff42SYuantian Tang struct qoriq_tmu_data *qdata = platform_get_drvdata(pdev); 1167797ff42SYuantian Tang int id, sites = 0; 11743528445SJia Hongtao 1187797ff42SYuantian Tang for (id = 0; id < SITES_MAX; id++) { 1197797ff42SYuantian Tang qdata->sensor[id] = devm_kzalloc(&pdev->dev, 1207797ff42SYuantian Tang sizeof(struct qoriq_sensor), GFP_KERNEL); 1217797ff42SYuantian Tang if (!qdata->sensor[id]) 1227797ff42SYuantian Tang return -ENOMEM; 12343528445SJia Hongtao 1247797ff42SYuantian Tang qdata->sensor[id]->id = id; 1257797ff42SYuantian Tang qdata->sensor[id]->qdata = qdata; 1267797ff42SYuantian Tang qdata->sensor[id]->tzd = devm_thermal_zone_of_sensor_register( 1277797ff42SYuantian Tang &pdev->dev, id, qdata->sensor[id], &tmu_tz_ops); 1287797ff42SYuantian Tang if (IS_ERR(qdata->sensor[id]->tzd)) { 1297797ff42SYuantian Tang if (PTR_ERR(qdata->sensor[id]->tzd) == -ENODEV) 1307797ff42SYuantian Tang continue; 1317797ff42SYuantian Tang else 1327797ff42SYuantian Tang return PTR_ERR(qdata->sensor[id]->tzd); 13343528445SJia Hongtao } 13443528445SJia Hongtao 1357797ff42SYuantian Tang sites |= 0x1 << (15 - id); 13643528445SJia Hongtao } 13743528445SJia Hongtao 1387797ff42SYuantian Tang /* Enable monitoring */ 1397797ff42SYuantian Tang if (sites != 0) 1407797ff42SYuantian Tang tmu_write(qdata, sites | TMR_ME | TMR_ALPF, &qdata->regs->tmr); 14143528445SJia Hongtao 1427797ff42SYuantian Tang return 0; 14343528445SJia Hongtao } 14443528445SJia Hongtao 14543528445SJia Hongtao static int qoriq_tmu_calibration(struct platform_device *pdev) 14643528445SJia Hongtao { 14743528445SJia Hongtao int i, val, len; 14843528445SJia Hongtao u32 range[4]; 14943528445SJia Hongtao const u32 *calibration; 15043528445SJia Hongtao struct device_node *np = pdev->dev.of_node; 15143528445SJia Hongtao struct qoriq_tmu_data *data = platform_get_drvdata(pdev); 15243528445SJia Hongtao 15343528445SJia Hongtao if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) { 15443528445SJia Hongtao dev_err(&pdev->dev, "missing calibration range.\n"); 15543528445SJia Hongtao return -ENODEV; 15643528445SJia Hongtao } 15743528445SJia Hongtao 15843528445SJia Hongtao /* Init temperature range registers */ 15943528445SJia Hongtao tmu_write(data, range[0], &data->regs->ttr0cr); 16043528445SJia Hongtao tmu_write(data, range[1], &data->regs->ttr1cr); 16143528445SJia Hongtao tmu_write(data, range[2], &data->regs->ttr2cr); 16243528445SJia Hongtao tmu_write(data, range[3], &data->regs->ttr3cr); 16343528445SJia Hongtao 16443528445SJia Hongtao calibration = of_get_property(np, "fsl,tmu-calibration", &len); 16543528445SJia Hongtao if (calibration == NULL || len % 8) { 16643528445SJia Hongtao dev_err(&pdev->dev, "invalid calibration data.\n"); 16743528445SJia Hongtao return -ENODEV; 16843528445SJia Hongtao } 16943528445SJia Hongtao 17043528445SJia Hongtao for (i = 0; i < len; i += 8, calibration += 2) { 17143528445SJia Hongtao val = of_read_number(calibration, 1); 17243528445SJia Hongtao tmu_write(data, val, &data->regs->ttcfgr); 17343528445SJia Hongtao val = of_read_number(calibration + 1, 1); 17443528445SJia Hongtao tmu_write(data, val, &data->regs->tscfgr); 17543528445SJia Hongtao } 17643528445SJia Hongtao 17743528445SJia Hongtao return 0; 17843528445SJia Hongtao } 17943528445SJia Hongtao 18043528445SJia Hongtao static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) 18143528445SJia Hongtao { 18243528445SJia Hongtao /* Disable interrupt, using polling instead */ 18343528445SJia Hongtao tmu_write(data, TIER_DISABLE, &data->regs->tier); 18443528445SJia Hongtao 18543528445SJia Hongtao /* Set update_interval */ 18643528445SJia Hongtao tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir); 18743528445SJia Hongtao 18843528445SJia Hongtao /* Disable monitoring */ 18943528445SJia Hongtao tmu_write(data, TMR_DISABLE, &data->regs->tmr); 19043528445SJia Hongtao } 19143528445SJia Hongtao 19243528445SJia Hongtao static int qoriq_tmu_probe(struct platform_device *pdev) 19343528445SJia Hongtao { 19443528445SJia Hongtao int ret; 19543528445SJia Hongtao struct qoriq_tmu_data *data; 19643528445SJia Hongtao struct device_node *np = pdev->dev.of_node; 19743528445SJia Hongtao 19843528445SJia Hongtao data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data), 19943528445SJia Hongtao GFP_KERNEL); 20043528445SJia Hongtao if (!data) 20143528445SJia Hongtao return -ENOMEM; 20243528445SJia Hongtao 20343528445SJia Hongtao platform_set_drvdata(pdev, data); 20443528445SJia Hongtao 20543528445SJia Hongtao data->little_endian = of_property_read_bool(np, "little-endian"); 20643528445SJia Hongtao 2074d82000aSAnson Huang data->regs = devm_platform_ioremap_resource(pdev, 0); 2084d82000aSAnson Huang if (IS_ERR(data->regs)) { 20943528445SJia Hongtao dev_err(&pdev->dev, "Failed to get memory region\n"); 2104d82000aSAnson Huang return PTR_ERR(data->regs); 21143528445SJia Hongtao } 21243528445SJia Hongtao 21351904045SAnson Huang data->clk = devm_clk_get_optional(&pdev->dev, NULL); 21451904045SAnson Huang if (IS_ERR(data->clk)) 21551904045SAnson Huang return PTR_ERR(data->clk); 21651904045SAnson Huang 21751904045SAnson Huang ret = clk_prepare_enable(data->clk); 21851904045SAnson Huang if (ret) { 21951904045SAnson Huang dev_err(&pdev->dev, "Failed to enable clock\n"); 22051904045SAnson Huang return ret; 22151904045SAnson Huang } 22251904045SAnson Huang 22343528445SJia Hongtao qoriq_tmu_init_device(data); /* TMU initialization */ 22443528445SJia Hongtao 22543528445SJia Hongtao ret = qoriq_tmu_calibration(pdev); /* TMU calibration */ 22643528445SJia Hongtao if (ret < 0) 2274d82000aSAnson Huang goto err; 22843528445SJia Hongtao 2297797ff42SYuantian Tang ret = qoriq_tmu_register_tmu_zone(pdev); 2307797ff42SYuantian Tang if (ret < 0) { 2317797ff42SYuantian Tang dev_err(&pdev->dev, "Failed to register sensors\n"); 2327797ff42SYuantian Tang ret = -ENODEV; 2334d82000aSAnson Huang goto err; 23443528445SJia Hongtao } 23543528445SJia Hongtao 23643528445SJia Hongtao return 0; 23743528445SJia Hongtao 2384d82000aSAnson Huang err: 23951904045SAnson Huang clk_disable_unprepare(data->clk); 24043528445SJia Hongtao platform_set_drvdata(pdev, NULL); 24143528445SJia Hongtao 24243528445SJia Hongtao return ret; 24343528445SJia Hongtao } 24443528445SJia Hongtao 24543528445SJia Hongtao static int qoriq_tmu_remove(struct platform_device *pdev) 24643528445SJia Hongtao { 24743528445SJia Hongtao struct qoriq_tmu_data *data = platform_get_drvdata(pdev); 24843528445SJia Hongtao 24943528445SJia Hongtao /* Disable monitoring */ 25043528445SJia Hongtao tmu_write(data, TMR_DISABLE, &data->regs->tmr); 25143528445SJia Hongtao 25251904045SAnson Huang clk_disable_unprepare(data->clk); 25351904045SAnson Huang 25443528445SJia Hongtao platform_set_drvdata(pdev, NULL); 25543528445SJia Hongtao 25643528445SJia Hongtao return 0; 25743528445SJia Hongtao } 25843528445SJia Hongtao 259aea59197SAnson Huang static int __maybe_unused qoriq_tmu_suspend(struct device *dev) 26043528445SJia Hongtao { 26143528445SJia Hongtao u32 tmr; 26243528445SJia Hongtao struct qoriq_tmu_data *data = dev_get_drvdata(dev); 26343528445SJia Hongtao 26443528445SJia Hongtao /* Disable monitoring */ 26543528445SJia Hongtao tmr = tmu_read(data, &data->regs->tmr); 26643528445SJia Hongtao tmr &= ~TMR_ME; 26743528445SJia Hongtao tmu_write(data, tmr, &data->regs->tmr); 26843528445SJia Hongtao 26951904045SAnson Huang clk_disable_unprepare(data->clk); 27051904045SAnson Huang 27143528445SJia Hongtao return 0; 27243528445SJia Hongtao } 27343528445SJia Hongtao 274aea59197SAnson Huang static int __maybe_unused qoriq_tmu_resume(struct device *dev) 27543528445SJia Hongtao { 27643528445SJia Hongtao u32 tmr; 27751904045SAnson Huang int ret; 27843528445SJia Hongtao struct qoriq_tmu_data *data = dev_get_drvdata(dev); 27943528445SJia Hongtao 28051904045SAnson Huang ret = clk_prepare_enable(data->clk); 28151904045SAnson Huang if (ret) 28251904045SAnson Huang return ret; 28351904045SAnson Huang 28443528445SJia Hongtao /* Enable monitoring */ 28543528445SJia Hongtao tmr = tmu_read(data, &data->regs->tmr); 28643528445SJia Hongtao tmr |= TMR_ME; 28743528445SJia Hongtao tmu_write(data, tmr, &data->regs->tmr); 28843528445SJia Hongtao 28943528445SJia Hongtao return 0; 29043528445SJia Hongtao } 29143528445SJia Hongtao 29243528445SJia Hongtao static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, 29343528445SJia Hongtao qoriq_tmu_suspend, qoriq_tmu_resume); 29443528445SJia Hongtao 29543528445SJia Hongtao static const struct of_device_id qoriq_tmu_match[] = { 29643528445SJia Hongtao { .compatible = "fsl,qoriq-tmu", }, 2976017e2a9SAnson Huang { .compatible = "fsl,imx8mq-tmu", }, 29843528445SJia Hongtao {}, 29943528445SJia Hongtao }; 30043528445SJia Hongtao MODULE_DEVICE_TABLE(of, qoriq_tmu_match); 30143528445SJia Hongtao 30243528445SJia Hongtao static struct platform_driver qoriq_tmu = { 30343528445SJia Hongtao .driver = { 30443528445SJia Hongtao .name = "qoriq_thermal", 30543528445SJia Hongtao .pm = &qoriq_tmu_pm_ops, 30643528445SJia Hongtao .of_match_table = qoriq_tmu_match, 30743528445SJia Hongtao }, 30843528445SJia Hongtao .probe = qoriq_tmu_probe, 30943528445SJia Hongtao .remove = qoriq_tmu_remove, 31043528445SJia Hongtao }; 31143528445SJia Hongtao module_platform_driver(qoriq_tmu); 31243528445SJia Hongtao 31343528445SJia Hongtao MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>"); 31443528445SJia Hongtao MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver"); 31543528445SJia Hongtao MODULE_LICENSE("GPL v2"); 316