diff options
author | Tang Yuantian <yuantian.tang@freescale.com> | 2014-03-03 02:03:30 (GMT) |
---|---|---|
committer | Jose Rivera <German.Rivera@freescale.com> | 2014-03-04 22:50:38 (GMT) |
commit | 0ee0cc5f198de9f205d8a4b373a4af9dd3cd62d9 (patch) | |
tree | 6ea16899696b224e57c4b15fd335d6ffc1e6fbdd /drivers/misc | |
parent | 9075df54c4773fee33ec62efeffcf91c406e6c87 (diff) | |
download | linux-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.c | 639 | ||||
-rw-r--r-- | drivers/misc/fsl_dcm.h | 129 |
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 |