summaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
authorTang Yuantian <yuantian.tang@freescale.com>2014-03-03 02:03:30 (GMT)
committerJose Rivera <German.Rivera@freescale.com>2014-03-04 22:50:38 (GMT)
commit0ee0cc5f198de9f205d8a4b373a4af9dd3cd62d9 (patch)
tree6ea16899696b224e57c4b15fd335d6ffc1e6fbdd /drivers/misc
parent9075df54c4773fee33ec62efeffcf91c406e6c87 (diff)
downloadlinux-fsl-qoriq-0ee0cc5f198de9f205d8a4b373a4af9dd3cd62d9.tar.xz
mpc85xx: unify the data collection module driver
This patch updated the data collection module driver. It removed all the hard-coded parameters by detecting automatically. Adding new board support will be easier in this way. Signed-off-by: Tang Yuantian <Yuantian.Tang@freescale.com> Change-Id: Ibb67c243faac53fee7865154e0fdb703933ace3f Reviewed-on: http://git.am.freescale.net:8181/8991 Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> Reviewed-by: Hongtao Jia <hongtao.jia@freescale.com> Reviewed-by: Jose Rivera <German.Rivera@freescale.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/fsl_dcm.c639
-rw-r--r--drivers/misc/fsl_dcm.h129
2 files changed, 442 insertions, 326 deletions
diff --git a/drivers/misc/fsl_dcm.c b/drivers/misc/fsl_dcm.c
index 88da605..1af2800 100644
--- a/drivers/misc/fsl_dcm.c
+++ b/drivers/misc/fsl_dcm.c
@@ -18,111 +18,19 @@
*
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/err.h>
#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/of_platform.h>
+#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
-
-/* sysfs commands for 'control' */
-#define SYSFS_DCM_CMD_STOP 0
-#define SYSFS_DCM_CMD_START 1
-
-/* Crecords can be either voltage, current, or temperature */
-enum crecord_type {
- CR_V, /* voltage */
- CR_C, /* current */
- CR_T /* temperature */
-};
-
-#define MAX_FREQUENCY 48 /* max freq (Hz) the DCM supports */
-#define DATA_ADDR 0x80 /* data address in DCM SRAM */
-
-struct crecord {
- __be16 curr; /* last sample read */
- __be16 max; /* maximum of all samples read */
- __be16 qty1; /* number of samples read */
- u8 qty2;
- __be32 acc; /* sum of all samples read */
-} __packed;
-#define MAX_CRECORDS ((0x100 - DATA_ADDR) / sizeof(struct crecord))
-
-struct om_info {
- __be16 version; /* DCM version number */
- u8 prescale; /* prescale value used (should be 0) */
- u8 timer; /* timer */
- u8 count; /* number of CRECORDS */
- __be16 address; /* address of CRECORD array in SRAM */
- u8 res[3];
-} __packed;
-
-#define MAX_FUNCTION 9
-/**
- * struct dcm_board - board-specific information
- * @compatible: the 'compatible' property to search for
- * @mask: enable mask for the OM_ENABLE command
- * @convert_iout: board-specific function to convert an IOUT to milliamps
- * @convert_tout: board-specific function to convert a TOUT to degrees Celsius
- * @ical: calibration factor for .convert_iout function
- * @num: number of crecords (equal to the number of '1's in @mask)
- * @names: array of names of each crecord
- * @types: array of types of each crecord
- * @voltage_fun: the index of voltage convert function
- */
-struct dcm_board {
- const char *compatible;
- u16 mask;
- unsigned int (*convert_iout)(u16 iout, unsigned int ical);
- unsigned int (*convert_tout)(u16 tout);
- unsigned int ical;
- unsigned int num;
- const char *names[MAX_CRECORDS];
- enum crecord_type types[MAX_CRECORDS];
- unsigned int voltage_fun[4];
-};
-
-/* PIXIS register status bits */
-#define PX_OCMD_MSG (1 << 0)
-#define PX_OACK_ERR (1 << 1)
-#define PX_OACK_ACK (1 << 0) /* OACK is sometimes called MACK */
-
-/* DCM commands */
-#define OM_END 0x00
-#define OM_SETDLY 0x01
-#define OM_RST0 0x02
-#define OM_RST1 0x03
-#define OM_CHKDLY 0x04
-#define OM_PWR 0x05
-#define OM_WAKE 0x07
-#define OM_GETMEM 0x08
-#define OM_SETMEM 0x09
-#define OM_SCLR 0x10
-#define OM_START 0x11
-#define OM_STOP 0x12
-#define OM_GET 0x13
-#define OM_ENABLE 0x14
-#define OM_TIMER 0x15
-#define OM_SETV 0x30
-#define OM_INFO 0x31
-
-struct fsl_dcm_data {
- struct device *dev;
- const struct dcm_board *board;
- void __iomem *base; /* PIXIS/QIXIS base address */
- u8 __iomem *addr; /* SRAM address */
- u8 __iomem *data; /* SRAM data */
- u8 __iomem *ocmd; /* DCM command/status */
- u8 __iomem *omsg; /* DCM message */
- u8 __iomem *mack; /* DCM acknowledge */
- struct crecord rec[MAX_CRECORDS];
- u8 timer;
- int running;
-};
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "fsl_dcm.h"
/*
* Converts a 16-bit VOUT value from the Zilker ZL6100 into a voltage value,
@@ -139,22 +47,21 @@ static unsigned int voltage_from_ina220(u16 vout)
return (vout >> 3) * 4;
}
-static unsigned int (*voltage_convert[2])(u16 vout) = {
- voltage_from_zl6100, voltage_from_ina220
-};
/*
* Converts a 16-bit IOUT from the Texas Instruments INA220 chip into a
- * current value, in milliamps. 'ical' is a board-specific calibration
- * factor.
+ * current value, in milliamps. 'shunt' is a board-specific shunt.
*/
-static unsigned int current_from_ina220(u16 vout, unsigned int ical)
+static unsigned int current_from_ina220(u16 sv, unsigned int shunt)
{
unsigned long c;
- /* milliamp = 1000 * ((vout / 100) / cal-factor) */
- /* = (vout * 100000) / ical */
- c = vout * 1000 * 100;
- c /= ical;
+ /*
+ * Current = ShuntVoltage * CalibrationRegister / 4096
+ * = ShuntVoltage * 40,960,000 / shunt(uOhms) / 4096
+ * = ShuntVoltage * 10000 / shunt
+ */
+ c = sv * 10000;
+ c /= shunt;
return c;
}
@@ -201,7 +108,7 @@ static int is_sram_available(struct fsl_dcm_data *dcm)
if ((cmd & PX_OCMD_MSG) || (ack & PX_OACK_ACK)) {
dev_dbg(dcm->dev, "dcm is not ready (cmd=%02X mack=%02X)\n",
- cmd, ack);
+ cmd, ack);
return 0;
}
@@ -220,8 +127,8 @@ static int run_program(struct fsl_dcm_data *dcm, u8 addr,
if (addr + len > 0xff) {
dev_err(dcm->dev, "address/length of %u/%u is out of bounds\n",
- addr, len);
- return 0;
+ addr, len);
+ return -EBUSY;
}
/* load the program into SRAM */
@@ -241,7 +148,7 @@ static int run_program(struct fsl_dcm_data *dcm, u8 addr,
50000, 1000);
if ((!v) || (v & PX_OACK_ERR)) {
dev_err(dcm->dev, "timeout or error waiting for start ack\n");
- return 0;
+ return -EBUSY;
}
/* 4. allow the host to read SRAM */
@@ -255,25 +162,25 @@ static int run_program(struct fsl_dcm_data *dcm, u8 addr,
/* 6. check for error or timeout */
if (v & (PX_OACK_ERR | PX_OACK_ACK)) {
dev_err(dcm->dev, "timeout or error waiting for stop ack\n");
- return 0;
+ return -EBUSY;
}
- return 1;
+ return 0;
}
#define TRATE0 241122 /* t-rate if prescale==0, in millihertz */
#define TRATE1 38579330 /* t-rate if prescale==1, in millihertz */
-
/*
* Empirical tests show that any frequency higher than 48Hz is unreliable.
*/
static int set_dcm_frequency(struct fsl_dcm_data *dcm,
- unsigned long frequency) {
+ unsigned long frequency)
+{
unsigned long timer;
if (!is_sram_available(dcm)) {
dev_err(dcm->dev, "dcm is busy\n");
- return 0;
+ return -EBUSY;
}
/* Restrict the frequency to a supported range. */
@@ -294,14 +201,14 @@ static int copy_from_sram(struct fsl_dcm_data *dcm, unsigned int addr,
if (addr + len > 0xff) {
dev_err(dcm->dev, "address/length of %u/%u is out of bounds\n",
- addr, len);
- return 0;
+ addr, len);
+ return -EBUSY;
}
for (i = 0; i < len; i++)
p[i] = read_sram(dcm, addr + i);
- return 1;
+ return 0;
}
/*
@@ -311,7 +218,7 @@ static int select_dcm_channels(struct fsl_dcm_data *dcm, u16 mask)
{
if (!is_sram_available(dcm)) {
dev_err(dcm->dev, "dcm is busy\n");
- return 0;
+ return -EBUSY;
}
return run_program(dcm, 0, 4, OM_ENABLE,
@@ -326,7 +233,7 @@ int start_data_collection(struct fsl_dcm_data *dcm)
{
if (!is_sram_available(dcm)) {
dev_err(dcm->dev, "dcm is busy\n");
- return 0;
+ return -EBUSY;
}
if (dcm->running)
@@ -337,63 +244,80 @@ int start_data_collection(struct fsl_dcm_data *dcm)
return run_program(dcm, 0, 4, OM_STOP, OM_SCLR, OM_START, OM_END);
}
-/*
- * Tells the DCM to stop data collection. Collected data is copied from
- * SRAM into a local buffer.
- */
+/* Tells the DCM to stop data collection. */
int stop_data_collection(struct fsl_dcm_data *dcm)
{
if (!dcm->running) {
dev_dbg(dcm->dev, "dcm is already stopped\n");
- return 1;
+ return 0;
}
if (!is_sram_available(dcm)) {
dev_err(dcm->dev, "dcm is busy\n");
- return 0;
+ return -EBUSY;
}
- if (!run_program(dcm, 0, 4, OM_STOP, OM_GET, DATA_ADDR, OM_END)) {
+ if (run_program(dcm, 0, 2, OM_STOP, OM_END)) {
dev_err(dcm->dev, "could not stop monitoring\n");
- return 0;
+ return -EBUSY;
}
- if (!copy_from_sram(dcm, DATA_ADDR, dcm->rec,
- dcm->board->num * sizeof(struct crecord))) {
- dev_err(dcm->dev, "could not copy sensor data\n");
- return 0;
+ dcm->running = false;
+
+ return 0;
+}
+
+static ssize_t dcm_get_info(struct device *dev)
+{
+ struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
+
+ if (!is_sram_available(dcm)) {
+ dev_err(dev, "dcm is busy\n");
+ return -EBUSY;
}
- dcm->running = 0;
- return 1;
+ if (run_program(dcm, 0, 3, OM_INFO, DATA_ADDR, OM_END)) {
+ dev_err(dev, "could not run 'info' program\n");
+ return -EBUSY;
+ }
+
+ if (copy_from_sram(dcm, DATA_ADDR, &dcm->board.info,
+ sizeof(struct om_info))) {
+ dev_err(dev, "could not copy 'info' data\n");
+ return -EBUSY;
+ }
+
+ return 0;
}
ssize_t fsl_dcm_sysfs_control_show(struct device *dev,
- struct device_attribute *attr, char *buf) {
+ struct device_attribute *attr, char *buf)
+{
struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", dcm->running ? "running" : "stoppped");
}
ssize_t fsl_dcm_sysfs_control_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count) {
+ struct device_attribute *attr, const char *buf, size_t count)
+{
struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
unsigned long pm_cmd;
int ret;
- ret = strict_strtoul(buf, 10, &pm_cmd);
+ ret = kstrtoul(buf, 10, &pm_cmd);
if (ret)
return ret;
switch (pm_cmd) {
case SYSFS_DCM_CMD_START:
ret = start_data_collection(dcm);
- if (!ret)
+ if (ret)
dev_err(dev, "failed to start power monitoring.\n");
break;
case SYSFS_DCM_CMD_STOP:
ret = stop_data_collection(dcm);
- if (!ret)
+ if (ret)
dev_err(dev, "failed to stop power monitoring\n");
break;
default:
@@ -403,81 +327,78 @@ ssize_t fsl_dcm_sysfs_control_store(struct device *dev,
return count;
}
+static int get_crecords(struct fsl_dcm_data *dcm)
+{
+ int len;
+ u8 addr1, addr2;
+ struct crecord *crec = dcm->board.crec;
+ struct om_info *info = &dcm->board.info;
+
+ /* get CRECORDs from ocm sram */
+ if (!is_sram_available(dcm)) {
+ dev_err(dcm->dev, "dcm is busy\n");
+ return -EBUSY;
+ }
+
+ len = sizeof(struct crecord) * info->count;
+ addr1 = (info->addr >> 8) & 0xff;
+ addr2 = info->addr & 0xff;
+
+ if (run_program(dcm, 0, 6, OM_GETMEMX, addr1,
+ addr2, len, DATA_ADDR, OM_END)) {
+ dev_err(dcm->dev, "could not stop monitoring\n");
+ return -EBUSY;
+ }
+
+ if (copy_from_sram(dcm, DATA_ADDR, crec, len)) {
+ dev_err(dcm->dev, "could not copy sensor data\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
/* Calculate the average, even if 'count' is zero */
#define AVG(sum, count) ((sum) / ((count) ?: 1))
#define MAX_RECORD 9
static ssize_t fsl_dcm_sysfs_result(struct device *dev,
- struct device_attribute *attr, char *buf) {
+ struct device_attribute *attr, char *buf)
+{
struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
- const struct dcm_board *board = dcm->board;
- unsigned int i, vindex;
+ struct record *rec = dcm->board.rec;
+ struct crecord *crec = dcm->board.crec;
ssize_t len;
- char *str;
- unsigned int num[MAX_RECORD], max[MAX_RECORD], avg[MAX_RECORD];
- const char *unit[MAX_RECORD];
+ int i, avg, num, val;
+
+ if (get_crecords(dcm))
+ return sprintf(buf, "Get record error\n");
len = sprintf(buf,
"Name Average\n"
"==================== ================\n");
- vindex = 0;
- for (i = 0; i < board->num; i++) {
- num[i] = (dcm->rec[i].qty1 << 8) | (dcm->rec[i].qty2);
-
- switch (board->types[i]) {
- case CR_V:
- max[i] = voltage_convert[board->voltage_fun[vindex]](
- dcm->rec[i].max);
- avg[i] = voltage_convert[board->voltage_fun[vindex]](
- AVG(dcm->rec[i].acc, num[i]));
- vindex++;
-
- unit[i] = "mV";
- break;
- case CR_C:
- max[i] = board->convert_iout(dcm->rec[i].max,
- board->ical);
- avg[i] = board->convert_iout(AVG(dcm->rec[i].acc,
- num[i]), board->ical);
- unit[i] = "mA";
- break;
- case CR_T:
- max[i] = board->convert_tout(dcm->rec[i].max);
- avg[i] = board->convert_tout(AVG(dcm->rec[i].acc,
- num[i]));
- unit[i] = "C ";
- break;
- default:
- continue;
+ for (i = 0; i < dcm->board.info.count; i++) {
+ num = (crec->num1 << 8) | (crec->num2);
+ val = AVG(crec->accum, num);
+
+ if (crec->ctl & CRCTL_GET_V) {
+ avg = voltage_from_zl6100(val);
+ } else if (crec->ctl & CRCTL_GET_T) {
+ avg = temp_from_u16(val);
+ } else if (crec->ctl & CRCTL_GET_V2) {
+ avg = voltage_from_ina220(val);
+ } else if (crec->ctl & CRCTL_GET_I2) {
+ avg = current_from_ina220(val, dcm->board.shunt);
+ } else {
+ dev_err(dev, "Unknown record\n");
+ return -EBUSY;
}
- }
- str = strstr(board->compatible, "tetra-fpga");
- if (str) { /* T4240 board */
- unsigned int idd;
-
- idd = avg[2] + avg[3] + avg[4] + avg[5];
- len += sprintf(buf + len,
- "CPU voltage: %-6u (mV)\n",
- avg[0]);
- len += sprintf(buf + len,
- "CPU current: %-6u (mA)\n",
- idd);
- len += sprintf(buf + len,
- "DDR voltage: %-6u (mV)\n",
- avg[6]);
- len += sprintf(buf + len,
- "DDR current: %-6u (mA)\n",
- avg[7]);
len += sprintf(buf + len,
- "CPU temperature: %-6u (C)\n",
- avg[8]);
-
- } else { /* for else */
- for (i = 0; i < board->num; i++)
- len += sprintf(buf + len,
"%-8s %-6d %s\n",
- board->names[i], avg[i], unit[i]);
+ rec->name, avg, rec->unit);
+ crec++;
+ rec++;
}
return len;
@@ -487,35 +408,21 @@ ssize_t fsl_dcm_sysfs_info(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
- struct om_info info;
+ struct om_info *info = &dcm->board.info;
ssize_t len;
- if (!is_sram_available(dcm)) {
- dev_err(dev, "dcm is busy\n");
- return 0;
- }
-
- if (!run_program(dcm, 0, 3, OM_INFO, DATA_ADDR, OM_END)) {
- dev_err(dev, "could not run 'info' program\n");
- return 0;
- }
-
- if (!copy_from_sram(dcm, DATA_ADDR, &info, sizeof(info))) {
- dev_err(dev, "could not copy 'info' data\n");
- return 0;
- }
-
- len = sprintf(buf, "DCM Version: %u\n", info.version);
- len += sprintf(buf + len, "Prescale: %u\n", info.prescale);
- len += sprintf(buf + len, "Timer: %u\n", info.timer);
- len += sprintf(buf + len, "Number of CRECORDs: %u\n", info.count);
- len += sprintf(buf + len, "CRECORD Address: %u\n", info.address);
+ len = sprintf(buf, "DCM Version: 0x%02x\n", info->ver);
+ len += sprintf(buf + len, "Prescale: %u\n", info->prescale);
+ len += sprintf(buf + len, "Timer: %u\n", info->timer);
+ len += sprintf(buf + len, "Number of CRECORDs: %u\n", info->count);
+ len += sprintf(buf + len, "CRECORD Address: 0x%04x\n", info->addr);
return len;
}
ssize_t fsl_dcm_sysfs_frequency_show(struct device *dev,
- struct device_attribute *attr, char *buf) {
+ struct device_attribute *attr, char *buf)
+{
struct fsl_dcm_data *dcm = dev_get_drvdata(dev);
unsigned long frequency;
@@ -531,7 +438,7 @@ ssize_t fsl_dcm_sysfs_frequency_store(struct device *dev,
unsigned long frequency;
int ret;
- ret = strict_strtoul(buf, 10, &frequency);
+ ret = kstrtoul(buf, 10, &frequency);
if (ret)
return ret;
@@ -557,14 +464,103 @@ static const struct attribute_group fsl_dcm_attr_group = {
},
};
+static int board_data_init(struct fsl_dcm_data *dcm)
+{
+ int i, len;
+ u8 addr1, addr2;
+ struct record *rec = dcm->board.rec;
+ struct crecord *crec = dcm->board.crec;
+ struct om_info *info = &dcm->board.info;
+ struct om_xinfo xinfo;
+ char name[MAX_NAME_LEN + 1];
+
+ /* 1. get CRECORD array from ocm */
+ if (get_crecords(dcm))
+ return -EBUSY;
+
+ /* 2. get xinfo located in CRECORD.xinfo_addr */
+ for (i = 0; i < info->count; i++) {
+ if (!is_sram_available(dcm)) {
+ dev_err(dcm->dev, "dcm is busy\n");
+ return -EBUSY;
+ }
+
+ addr1 = (crec->xinfo_addr >> 8) & 0xff;
+ addr2 = (crec->xinfo_addr) & 0xff;
+ len = sizeof(struct om_xinfo);
+
+ if (run_program(dcm, 0, 6, OM_GETMEMX, addr1,
+ addr2, len, DATA_ADDR, OM_END)) {
+ dev_err(dcm->dev, "get xinfo error\n");
+ return -EBUSY;
+ }
+
+ if (copy_from_sram(dcm, DATA_ADDR, &xinfo, len)) {
+ dev_err(dcm->dev, "could not copy xinfo data\n");
+ return -EBUSY;
+ }
+
+ /* 3. get record name in struct xinfo */
+ if (!is_sram_available(dcm)) {
+ dev_err(dcm->dev, "dcm is busy\n");
+ return -EBUSY;
+ }
+
+ addr1 = (xinfo.name_addr >> 8) & 0xff;
+ addr2 = (xinfo.name_addr) & 0xff;
+ len = MAX_NAME_LEN;
+
+ if (run_program(dcm, 0, 6, OM_GETMEMX, addr1,
+ addr2, len, DATA_ADDR, OM_END)) {
+ dev_err(dcm->dev, "\n");
+ return -EBUSY;
+ }
+
+ if (copy_from_sram(dcm, DATA_ADDR, name, len)) {
+ dev_err(dcm->dev, "could not copy record name data\n");
+ return -EBUSY;
+ }
+
+ name[MAX_NAME_LEN] = 0;
+ rec->name = kstrdup(name, GFP_KERNEL);
+
+ /* assign the unit according record type */
+ if ((crec->ctl & CRCTL_GET_V) || (crec->ctl & CRCTL_GET_V2)) {
+ rec->unit = "mV";
+ } else if (crec->ctl & CRCTL_GET_T) {
+ rec->unit = "C ";
+ } else if (crec->ctl & CRCTL_GET_I2) {
+ rec->unit = "mA";
+ } else {
+ dev_err(dcm->dev, "Unknown record\n");
+ return -EBUSY;
+ }
+
+ /* deal with next record */
+ rec++;
+ crec++;
+ }
+
+ return 0;
+}
+
+/* .data is shunt value(in uOhms) of ina220 */
+static struct of_device_id fsl_dcm_ids[] = {
+ { .compatible = "fsl,p1022ds-fpga", .data = 0},
+ { .compatible = "fsl,p5020ds-fpga", .data = (void *)2106},
+ { .compatible = "fsl,tetra-fpga", .data = (void *)1000},
+ {}
+};
+
static int fsl_dcm_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct fsl_dcm_data *dcm;
+ const struct of_device_id *match;
int ret;
u8 ver;
- dcm = kzalloc(sizeof(struct fsl_dcm_data), GFP_KERNEL);
+ dcm = kzalloc(sizeof(*dcm), GFP_KERNEL);
if (!dcm)
return -ENOMEM;
@@ -572,15 +568,13 @@ static int fsl_dcm_probe(struct platform_device *pdev)
if (!dcm->base) {
dev_err(&pdev->dev, "could not map fpga node\n");
ret = -ENOMEM;
- goto error_kzalloc;
+ goto err_kzalloc;
}
- dcm->dev = &pdev->dev;
- dcm->board = pdev->dev.platform_data;
-
/*
+ * Get GMSA version
+ *
* write 0x1F to GDC register then read GDD register
- * to get GMSA version.
* 0x00: v1 -> pixis
* 0x01: v2 -> qixis
*/
@@ -593,7 +587,6 @@ static int fsl_dcm_probe(struct platform_device *pdev)
dcm->addr = dcm->base + 0x12;
dcm->data = dcm->base + 0x13;
}
-
dcm->ocmd = dcm->base + 0x14;
dcm->omsg = dcm->base + 0x15;
dcm->mack = dcm->base + 0x18;
@@ -601,40 +594,91 @@ static int fsl_dcm_probe(struct platform_device *pdev)
/* Check to make sure the DCM is enable and working */
if (!is_sram_available(dcm)) {
dev_err(&pdev->dev, "dcm is not responding\n");
- ret = -ENODEV;
- goto error_iomap;
+ ret = -EBUSY;
+ goto err_iomap;
}
+ dcm->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, dcm);
- ret = sysfs_create_group(&pdev->dev.kobj, &fsl_dcm_attr_group);
+ /* get the struct om_info */
+ if (dcm_get_info(&pdev->dev)) {
+ dev_err(&pdev->dev, "could not get struct om_info\n");
+ ret = -EBUSY;
+ goto err_iomap;
+ }
+
+ /* only support v41 or later */
+ if (dcm->board.info.ver < 41 || dcm->board.info.ver == 0xff) {
+ dev_err(&pdev->dev, "dcm is invalid or needs to update\n");
+ ret = -ENODEV;
+ goto err_iomap;
+ }
+
+ dcm->board.rec = kzalloc(sizeof(*dcm->board.rec) *
+ dcm->board.info.count, GFP_KERNEL);
+ if (!dcm->board.rec) {
+ dev_err(&pdev->dev, "no memory\n");
+ ret = -ENOMEM;
+ goto err_iomap;
+ }
+
+ dcm->board.crec = kzalloc(sizeof(*dcm->board.crec) *
+ dcm->board.info.count, GFP_KERNEL);
+ if (!dcm->board.crec) {
+ dev_err(&pdev->dev, "no memory\n");
+ ret = -ENOMEM;
+ goto err_kzall;
+ }
+
+ /* get the shunt value */
+ match = of_match_node(fsl_dcm_ids, np);
+ dcm->board.shunt = (long)match->data;
+
+ /* enable all the channel */
+ dcm->board.mask = 0x1FF;
+
+ /* init all the board specific data */
+ ret = board_data_init(dcm);
if (ret) {
dev_err(&pdev->dev, "could not create sysfs group\n");
- goto error_iomap;
+ ret = -ENODEV;
+ goto err_kzall2;
}
- if (!select_dcm_channels(dcm, dcm->board->mask)) {
+ /* enable all the channel */
+ if (select_dcm_channels(dcm, dcm->board.mask)) {
dev_err(&pdev->dev, "could not set crecord mask\n");
ret = -ENODEV;
- goto error_sysfs;
+ goto err_kzall2;
}
- /* Set the timer to the fastest support rate. */
- if (!set_dcm_frequency(dcm, 1)) {
+ /* Set the timer to the 1 Hz */
+ if (set_dcm_frequency(dcm, 1)) {
dev_err(&pdev->dev, "could not set frequency\n");
ret = -ENODEV;
- goto error_sysfs;
+ goto err_kzall2;
+ }
+
+ /* create sysfs interface */
+ ret = sysfs_create_group(&pdev->dev.kobj, &fsl_dcm_attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "could not create sysfs group\n");
+ goto err_kzall2;
}
return 0;
-error_sysfs:
- sysfs_remove_group(&pdev->dev.kobj, &fsl_dcm_attr_group);
+err_kzall2:
+ kfree(dcm->board.crec);
+
+err_kzall:
+ kfree(dcm->board.rec);
-error_iomap:
+err_iomap:
iounmap(dcm->base);
-error_kzalloc:
+err_kzalloc:
kfree(dcm);
return ret;
@@ -645,126 +689,69 @@ static int fsl_dcm_remove(struct platform_device *pdev)
struct fsl_dcm_data *dcm = dev_get_drvdata(&pdev->dev);
stop_data_collection(dcm);
-
sysfs_remove_group(&pdev->dev.kobj, &fsl_dcm_attr_group);
-
iounmap(dcm->base);
+ kfree(dcm->board.rec);
+ kfree(dcm->board.crec);
kfree(dcm);
return 0;
}
-static struct platform_driver fsl_dcm_platform_driver = {
- .probe = fsl_dcm_probe,
- .remove = fsl_dcm_remove,
- .driver = {
- .name = "fsl-dcm",
+static struct platform_driver fsl_dcm_driver = {
+ .driver = {
+ .name = "fsl-dcm-driver",
.owner = THIS_MODULE,
},
-};
-
-static const struct dcm_board dcm_types[] = {
- { /* 4-v, 1-t */
- "fsl,p1022ds-fpga",
- 0x155,
- NULL,
- temp_from_u16,
- 0,
- 5,
- /* Current measurements are not reliable on this board */
- {"Vdd", "OVdd", "S/XVdd", "GVdd", "CPU_Tj"},
- {CR_V, CR_V, CR_V, CR_V, CR_T},
- {0, 0, 0, 0},
- },
- { /* 4-v, 4-i, 1-t */
- "fsl,p5020ds-fpga",
- 0x1ff,
- current_from_ina220,
- temp_from_u16,
- 21064,
- 9,
- {"Vdd_CA", "Idd_CA", "Vdd_CB", "Idd_CB", "Vdd_PL", "Idd_PL",
- "GVdd", "GIdd", "CPU_Tj"},
- {CR_V, CR_C, CR_V, CR_C, CR_V, CR_C, CR_V, CR_C, CR_T},
- {0, 0, 0, 0},
- },
- { /* 2-v, 6-c, 1-t */
- "fsl,tetra-fpga",
- 0x1ff,
- current_from_ina220,
- temp_from_u16,
- 10000,
- 9,
- {"VDD", "IDD", "IDD_ph0", "IDD_ph1", "IDD_ph2", "IDD_ph3",
- "GVDD", "GIDD", "TEMP"},
- {CR_V, CR_C, CR_C, CR_C, CR_C, CR_C, CR_V, CR_C, CR_T},
- {1, 0},
- },
+ .probe = fsl_dcm_probe,
+ .remove = fsl_dcm_remove,
};
static int __init fsl_dcm_init(void)
{
+ struct device_node *np;
struct platform_device *pdev;
- struct device_node *np = NULL;
- unsigned int i;
int ret;
- /* Look for a supported platform */
- for (i = 0; i < ARRAY_SIZE(dcm_types); i++) {
- np = of_find_compatible_node(NULL, NULL,
- dcm_types[i].compatible);
- if (np)
- break;
- }
- if (!np) {
- pr_debug("fsl-dcm: unsupported platform\n");
+ np = of_find_matching_node(NULL, fsl_dcm_ids);
+ if (!np)
return -ENODEV;
- }
/* We found a supported platform, so register a platform driver */
- ret = platform_driver_register(&fsl_dcm_platform_driver);
+ ret = platform_driver_register(&fsl_dcm_driver);
if (ret) {
pr_err("fsl-dcm: could not register platform driver\n");
- goto error_np;
+ goto err_np;
}
/* We need to create a device and add the data for this platform */
- pdev = platform_device_alloc(fsl_dcm_platform_driver.driver.name, 0);
+ pdev = platform_device_alloc(fsl_dcm_driver.driver.name, 0);
if (!pdev) {
ret = -ENOMEM;
- goto error_drv;
+ goto err_drv;
}
/* Pass the device_node pointer to the probe function */
pdev->dev.of_node = np;
- /* Pass the DCM platform data */
- ret = platform_device_add_data(pdev, &dcm_types[i],
- sizeof(struct dcm_board));
- if (ret) {
- pr_err("fsl-dcm: could not register platform driver\n");
- goto error_dev;
- }
-
/* This will call the probe function */
ret = platform_device_add(pdev);
if (ret) {
pr_err("fsl-dcm: could not register platform driver\n");
- goto error_dev;
+ goto err_dev;
}
- of_node_put(np);
-
pr_info("Freescale Data Collection Module is installed.\n");
+
return 0;
-error_dev:
+err_dev:
platform_device_unregister(pdev);
-error_drv:
- platform_driver_unregister(&fsl_dcm_platform_driver);
+err_drv:
+ platform_driver_unregister(&fsl_dcm_driver);
-error_np:
+err_np:
of_node_put(np);
return ret;
@@ -772,7 +759,7 @@ error_np:
static void __exit fsl_dcm_exit(void)
{
- platform_driver_unregister(&fsl_dcm_platform_driver);
+ platform_driver_unregister(&fsl_dcm_driver);
}
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
diff --git a/drivers/misc/fsl_dcm.h b/drivers/misc/fsl_dcm.h
new file mode 100644
index 0000000..722ac28
--- /dev/null
+++ b/drivers/misc/fsl_dcm.h
@@ -0,0 +1,129 @@
+#ifndef _FSL_DCM_H
+#define _FSL_DCM_H
+
+/* sysfs commands for 'control' */
+#define SYSFS_DCM_CMD_STOP 0
+#define SYSFS_DCM_CMD_START 1
+
+#define MAX_NAME_LEN 10
+#define MAX_FREQUENCY 48 /* max freq the DCM supports */
+#define DATA_ADDR 0x20 /* data address in DCM SRAM */
+#define CHAN_MASK 0x1FF /* 9 channel is enabled */
+
+/* PIXIS register status bits */
+#define PX_OCMD_MSG (1 << 0)
+#define PX_OACK_ERR (1 << 1)
+#define PX_OACK_ACK (1 << 0) /* OACK is sometimes called MACK */
+
+/* DCM commands */
+#define OM_END 0x00
+#define OM_SETDLY 0x01
+#define OM_RST0 0x02
+#define OM_RST1 0x03
+#define OM_CHKDLY 0x04
+#define OM_PWR 0x05
+#define OM_WAKE 0x07
+#define OM_GETMEM 0x08
+#define OM_SETMEM 0x09
+#define OM_SCLR 0x10
+#define OM_START 0x11
+#define OM_STOP 0x12
+#define OM_GET 0x13
+#define OM_ENABLE 0x14
+#define OM_TIMER 0x15
+#define OM_SNAP 0x16
+#define OM_GETMEMX 0x18
+#define OM_AVGOFF 0x19
+#define OM_AVGON 0x1A
+#define OM_SETV 0x30
+#define OM_INFO 0x31
+#define OM_XINFO 0x32
+
+/* Support definitions */
+#define CRCTLBIT_ENA (7) /* Set to enable collection */
+#define CRCTLBIT_IGNORE (6) /* Ignore record for voltage mangement */
+#define CRCTLBIT_FILTER (5) /* Set to enable data filtering */
+#define CRCTLBIT_GET_V2 (4) /* Record type: voltage via INA220 */
+#define CRCTLBIT_GET_I2 (3) /* Record type: current via INA220 */
+#define CRCTLBIT_GET_T (2) /* Record type: temperature via ADT7461 */
+#define CRCTLBIT_GET_I (1) /* Record type: current via PMBus device */
+#define CRCTLBIT_GET_V (0) /* Record type: voltage via PMBus device */
+
+/* Bitmasks */
+#define CRCTL_ENA (1 << CRCTLBIT_ENA)
+#define CRCTL_IGNORE (1 << CRCTLBIT_IGNORE)
+#define CRCTL_FILTER (1 << CRCTLBIT_FILTER)
+#define CRCTL_GET_V2 (1 << CRCTLBIT_GET_V2)
+#define CRCTL_GET_I2 (1 << CRCTLBIT_GET_I2)
+#define CRCTL_GET_T (1 << CRCTLBIT_GET_T)
+#define CRCTL_GET_I (1 << CRCTLBIT_GET_I)
+#define CRCTL_GET_V (1 << CRCTLBIT_GET_V)
+
+/* Handy definitions */
+#define CR_VOLT_PMB (CRCTL_ENA|CRCTL_FILTER|CRCTL_GET_V)
+#define CR_VOLT_PMB_X (CRCTL_ENA|CRCTL_FILTER|CRCTL_GET_V|CRCTL_IGNORE)
+#define CR_CURR_PMB (CRCTL_ENA|CRCTL_FILTER|CRCTL_GET_I)
+#define CR_VOLT_INA (CRCTL_ENA|CRCTL_GET_V2)
+#define CR_CURR_INA (CRCTL_ENA|CRCTL_GET_I2)
+#define CR_TEMP (CRCTL_ENA|CRCTL_FILTER|CRCTL_GET_T)
+
+struct crecord {
+ u8 ctl; /* enabled, data type */
+ u8 i2c_addr; /* target i2c address */
+ u8 port; /* target port(channel) */
+ __be16 curr; /* most recent sample */
+ __be16 max; /* maximum value */
+ __be16 num1; /* number of samples taken */
+ u8 num2;
+ __be32 accum; /* sum of samples */
+ __be16 xinfo_addr; /* pointer to XINFO structure */
+} __packed;
+
+struct om_info {
+ __be16 ver; /* DCM version number */
+ u8 prescale; /* prescale value used */
+ u8 timer; /* timer */
+ u8 count; /* number of CRECORDS */
+ __be16 addr; /* address of CRECORD array in SRAM */
+ u8 res[3];
+} __packed;
+
+struct om_xinfo {
+ __be16 name_addr; /* descriptive name */
+ __be16 def; /* SW-defined default */
+ __be16 curr; /* SW-obtained value */
+ __be16 cal; /* calibration factor */
+ u8 flags;
+ u8 method;
+ __be16 get; /* get current value */
+ __be16 set; /* set voltage to value */
+ __be16 init; /* initialize device */
+} __packed;
+
+struct record {
+ char *name; /* record name */
+ char *unit; /* record unit */
+};
+
+struct dcm_board {
+ struct om_info info;
+ struct record *rec; /* per record struct */
+ struct crecord *crec; /* all the collected records */
+ u32 shunt; /* shunt for ina220 current */
+ u32 mask; /* channel mask */
+};
+
+struct fsl_dcm_data {
+ void __iomem *base; /* PIXIS/QIXIS base address */
+ struct device *dev;
+ u8 __iomem *addr; /* SRAM address */
+ u8 __iomem *data; /* SRAM data */
+ u8 __iomem *ocmd; /* DCM command/status */
+ u8 __iomem *omsg; /* DCM message */
+ u8 __iomem *mack; /* DCM acknowledge */
+ u8 timer;
+ int running;
+ struct dcm_board board;
+};
+
+#endif