From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from sender163-mail.zoho.com (sender163-mail.zoho.com [74.201.84.163]) (using TLSv1 with cipher ECDHE-RSA-AES128-SHA (128/128 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 0639E1A072B for ; Thu, 12 Nov 2015 23:10:53 +1100 (AEDT) Received: from localhost (172.110.7.206 [172.110.7.206]) by mx.zohomail.com with SMTPS id 1447327227386977.6177895729468; Thu, 12 Nov 2015 03:20:27 -0800 (PST) From: OpenBMC Patches To: openbmc@lists.ozlabs.org Cc: adamliyi Subject: [PATCH openbmc v4 3/5] initial check in for occ hwmon driver Date: Thu, 12 Nov 2015 06:20:17 -0500 Message-Id: <1447327219-20318-4-git-send-email-openbmc-patches@stwcx.xyz> X-Mailer: git-send-email 2.6.3 In-Reply-To: <1447327219-20318-1-git-send-email-openbmc-patches@stwcx.xyz> References: <1447327219-20318-1-git-send-email-openbmc-patches@stwcx.xyz> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 12 Nov 2015 12:10:54 -0000 From: adamliyi --- .../conf/machine/include/openpower.inc | 2 + .../meta-openpower/i2c-occ-mod/bk.files/COPYING | 340 ++++++++ .../meta-openpower/i2c-occ-mod/bk.files/Makefile | 14 + .../meta-openpower/i2c-occ-mod/bk.files/occ.c | 942 +++++++++++++++++++++ .../meta-openpower/i2c-occ-mod/i2c-occ-mod_0.1.bb | 19 + 5 files changed, 1317 insertions(+) create mode 100644 meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/COPYING create mode 100644 meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/Makefile create mode 100644 meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/occ.c create mode 100644 meta-openbmc-machines/meta-openpower/i2c-occ-mod/i2c-occ-mod_0.1.bb diff --git a/meta-openbmc-machines/meta-openpower/conf/machine/include/openpower.inc b/meta-openbmc-machines/meta-openpower/conf/machine/include/openpower.inc index 48edd92..dd01714 100644 --- a/meta-openbmc-machines/meta-openpower/conf/machine/include/openpower.inc +++ b/meta-openbmc-machines/meta-openpower/conf/machine/include/openpower.inc @@ -30,3 +30,5 @@ PREFERRED_PROVIDER_virtual/obmc-phosphor-flash-mgmt = "skeleton" PREFERRED_PROVIDER_virtual/obmc-phosphor-policy-mgmt = "skeleton" PREFERRED_PROVIDER_virtual/obmc-phosphor-sensor-mgmt = "skeleton" PREFERRED_PROVIDER_virtual/obmc-phosphor-system-mgmt = "skeleton" + +MACHINE_EXTRA_RRECOMMENDS += "kernel-module-occ" diff --git a/meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/COPYING b/meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/COPYING new file mode 100644 index 0000000..6d45519 --- /dev/null +++ b/meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/Makefile b/meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/Makefile new file mode 100644 index 0000000..232508c --- /dev/null +++ b/meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/Makefile @@ -0,0 +1,14 @@ +obj-m := occ.o + +SRC := $(shell pwd) + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) + +modules_install: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install + +clean: + rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c + rm -f Module.markers Module.symvers modules.order + rm -rf .tmp_versions Modules.symvers diff --git a/meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/occ.c b/meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/occ.c new file mode 100644 index 0000000..9f0a6c0 --- /dev/null +++ b/meta-openbmc-machines/meta-openpower/i2c-occ-mod/bk.files/occ.c @@ -0,0 +1,942 @@ +/* + * BMC OCC HWMON driver - read Power8 OCC (On Chip Controller) sensor data via i2c. + * + * Copyright (c) 2015 IBM (Alvin Wang, Li Yi) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 1 + +/* ------------------------------------------------------------*/ +/* OCC sensor data format */ +typedef struct { + uint16_t sensor_id; + uint16_t value; +} occ_sensor; + +typedef struct { + uint16_t sensor_id; + uint32_t update_tag; + uint32_t accumulator; + uint16_t value; +} powr_sensor; + + +typedef struct { + char sensor_type[5]; + uint8_t reserved0; + uint8_t sensor_format; + uint8_t sensor_length; + uint8_t num_of_sensors; + occ_sensor *sensor; + powr_sensor *powr; +} sensor_data_block; + +typedef struct { + uint8_t status; + uint8_t ext_status; + uint8_t occs_present; + uint8_t config; + uint8_t occ_state; + uint8_t reserved0; + uint8_t reserved1; + uint8_t error_log_id; + uint32_t error_log_addr_start; + uint16_t error_log_length; + uint8_t reserved2; + uint8_t reserved3; + char occ_code_level[17]; + char sensor_eye_catcher[7]; + uint8_t num_of_sensor_blocks; + uint8_t sensor_data_version; + sensor_data_block* blocks; +} occ_poll_data; + +typedef struct { + uint8_t sequence_num; + uint8_t cmd_type; + uint8_t rtn_status; + uint16_t data_length; + occ_poll_data data; + uint16_t chk_sum; + uint16_t temp_block_id; + uint16_t freq_block_id; + uint16_t power_block_id; +} occ_response_t; + +static occ_response_t occ_resp; + +/* Each client has this additional data */ +struct occ_drv_data { + struct i2c_client *client; + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if sensor data are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long sample_time; /* In jiffies */ + occ_response_t *occ_resp; +}; + +/*-----------------------------------------------------------------------*/ +/* i2c read and write occ sensors */ + +#define OCC_DATA_MAX 4096 /* 4KB at most */ +#define I2C_STATUS_REG 0x000d0001 +#define I2C_ERROR_REG 0x000d0002 +#define I2C_READ_ERROR 1 +#define I2C_WRITE_ERROR 2 +#define I2C_DATABUFFER_SIZE_ERROR 3 + +/* +#define SCOM_OCC_SRAM_WOX 0x0006B013 +#define SCOM_OCC_SRAM_WAND 0x0006B012 +#define SCOM_OCC_SRAM_ADDR 0x0006B010 +#define SCOM_OCC_SRAM_DATA 0x0006B015 +*/ + +// To generate attn to OCC +#define ATTN_DATA 0x0006B035 + +// For BMC to read/write SRAM +#define OCB_ADDRESS 0x0006B070 +#define OCB_DATA 0x0006B075 +#define OCB_STATUS_CONTROL_AND 0x0006B072 +#define OCB_STATUS_CONTROL_OR 0x0006B073 + +#define OCC_COMMAND_ADDR 0xFFFF6000 +#define OCC_RESPONSE_ADDR 0xFFFF7000 + + +static int deinit_occ_resp_buf(occ_response_t *p) +{ + int b; + + if (p == NULL) + return 0; + + if (p->data.blocks == NULL) + return 0; + + for(b = 0; b < p->data.num_of_sensor_blocks; b++) { + if (!p->data.blocks[b].sensor) + kfree(p->data.blocks[b].sensor); + if (!p->data.blocks[b].powr) + kfree(p->data.blocks[b].powr); + } + + kfree(p->data.blocks); + + memset(p, 0, sizeof(*p)); + + return 0; +} + +static ssize_t occ_i2c_read(struct i2c_client *client, char *buf, size_t count) +{ + int ret = 0; + + if (count > 8192) + count = 8192; + + pr_debug("i2c_read: reading %zu bytes.\n", count); + ret = i2c_master_recv(client, buf, count); + return ret; +} + +static ssize_t occ_i2c_write(struct i2c_client *client, const char *buf, size_t count) +{ + int ret = 0; + + if (count > 8192) + count = 8192; + + pr_debug("i2c_write: writing %zu bytes.\n", count); + ret = i2c_master_send(client, buf, count); + return ret; +} + +/* read two 4-byte value */ +static int occ_getscom(struct i2c_client *client, uint32_t address, uint32_t *value0, uint32_t *value1) +{ + uint32_t ret = 0; + char buf[8]; + const char* address_buf = (const char*)&address; + + //P8 i2c slave requires address to be shifted by 1 + address = address << 1; + + ret = occ_i2c_write(client, address_buf, sizeof(address)); + //if (ret != sizeof(address)) + // return -I2C_WRITE_ERROR; + + ret = occ_i2c_read(client, buf, sizeof(buf)); + //if (ret != sizeof(buf)) + // return -I2C_READ_ERROR; + + memcpy(value1, &buf[0], sizeof(*value1)); + memcpy(value0, &buf[4], sizeof(*value0)); + + return 0; +} + +/* read 8-byte value and put into data[offset] */ +static int occ_getscomb(struct i2c_client *client, uint32_t address, char* data, int offset) +{ + uint32_t ret = 0; + const char* address_buf = (const char*)&address; + char buf[8]; + int b = 0; + + //P8 i2c slave requires address to be shifted by 1 + address = address << 1; + + ret = occ_i2c_write(client, address_buf, sizeof(address)); + //if (ret != sizeof(address)) + // return -I2C_WRITE_ERROR; + + ret = occ_i2c_read(client, buf, sizeof(buf)); + //if (ret != sizeof(buf)) + // return -I2C_READ_ERROR; + + for (b = 0; b < 8; b++) { + data[offset + b] = buf[7 - b]; + } + + return 0; +} + +static int occ_putscom(struct i2c_client *client, uint32_t address, uint32_t data0, uint32_t data1) +{ + const char* address_buf = (const char*)&address; + const char* d0 = (const char*)&data0; + const char* d1 = (const char*)&data1; + char buf[12]; + uint32_t ret = 0; + + //P8 i2c slave requires address to be shifted by 1 + address = address << 1; + + memcpy(&buf[0], address_buf, sizeof(address)); + memcpy(&buf[4], d1, sizeof(data1)); + memcpy(&buf[8], d0, sizeof(data0)); + + ret = occ_i2c_write(client, buf, sizeof(buf)); + //if (ret != sizeof(buf)) + // return I2C_WRITE_ERROR; + + return 0; +} + +static int occ_check_i2c_errors(struct i2c_client *client) +{ + uint32_t v0; + uint32_t v1; + + occ_getscom(client, I2C_STATUS_REG, &v0, &v1); + if (v0 != 0x80000000) { + printk("ERROR present in P8 I2C Slave. Clearing...\n"); + occ_putscom(client, I2C_ERROR_REG, 0x00000000, 0x00000000); + occ_putscom(client, I2C_STATUS_REG, 0x00000000, 0x00000000); + return -1; + } + + return 0; +} + + +static inline uint16_t get_occdata_length(char* d) +{ + uint16_t data_length = 0; + + data_length = d[3] << 8; + data_length = data_length | d[4]; + return data_length; +} + + +static int parse_occ_response(char* d, occ_response_t* o) +{ + int b = 0; + int s = 0; + int ret = 0; + int dnum = 45; + + o->sequence_num = d[0]; + o->cmd_type = d[1]; + o->rtn_status = d[2]; + o->data_length = d[3] << 8; + o->data_length = o->data_length | d[4]; + o->data.status = d[5]; + o->data.ext_status = d[6]; + o->data.occs_present = d[7]; + o->data.config = d[8]; + o->data.occ_state = d[9]; + o->data.reserved0 = d[10]; + o->data.reserved1 = d[11]; + o->data.error_log_id = d[12]; + o->data.error_log_addr_start = d[13] << 24; + o->data.error_log_addr_start = o->data.error_log_addr_start | d[14] << 16; + o->data.error_log_addr_start = o->data.error_log_addr_start | d[15] << 8; + o->data.error_log_addr_start = o->data.error_log_addr_start | d[16]; + o->data.error_log_length = d[17] << 8; + o->data.error_log_length = o->data.error_log_length | d[18]; + o->data.reserved2 = d[19]; + o->data.reserved3 = d[20]; + strncpy(&o->data.occ_code_level[0], (const char*)&d[21], 16); + strncpy(&o->data.sensor_eye_catcher[0], (const char*)&d[37], 6); + o->data.sensor_eye_catcher[6]='\0'; + o->data.num_of_sensor_blocks=d[43]; + o->data.sensor_data_version = d[44]; + + if (strcmp(o->data.sensor_eye_catcher, "SENSOR") != 0) { + printk("ERROR: SENSOR not found at byte 37 (%s)\n",o->data.sensor_eye_catcher); + return -1; + } + + if (o->data.num_of_sensor_blocks == 0) { + printk("ERROR: SENSOR block num is 0\n"); + return -1; + } + + o->data.blocks = kzalloc(sizeof(sensor_data_block) * o->data.num_of_sensor_blocks, GFP_KERNEL); + if (o->data.blocks == NULL) + return -ENOMEM; + + printk("Reading %d sensor blocks\n", o->data.num_of_sensor_blocks); + for(b = 0; b < o->data.num_of_sensor_blocks; b++) { + /* 8-byte sensor block head */ + strncpy(&o->data.blocks[b].sensor_type[0], (const char*)&d[dnum], 4); + o->data.blocks[b].reserved0 = d[dnum+4]; + o->data.blocks[b].sensor_format = d[dnum+5]; + o->data.blocks[b].sensor_length = d[dnum+6]; + o->data.blocks[b].num_of_sensors = d[dnum+7]; + dnum = dnum + 8; + + printk("sensor block[%d]: type: %s, num_of_sensors: %d, sensor_length: %u\n", + b, o->data.blocks[b].sensor_type, o->data.blocks[b].num_of_sensors, + o->data.blocks[b].sensor_length); + + /* empty sensor block */ + if (o->data.blocks[b].num_of_sensors <= 0) + continue; + if (o->data.blocks[b].sensor_length == 0) + continue; + + if (strcmp(o->data.blocks[b].sensor_type, "FREQ") == 0) { + o->data.blocks[b].sensor = + kzalloc(sizeof(occ_sensor) * o->data.blocks[b].num_of_sensors, GFP_KERNEL); + + if (o->data.blocks[b].sensor == NULL) { + ret = -ENOMEM; + goto abort; + } + o->freq_block_id = b; + for (s = 0; s < o->data.blocks[b].num_of_sensors; s++) { + o->data.blocks[b].sensor[s].sensor_id = d[dnum] << 8; + o->data.blocks[b].sensor[s].sensor_id = + o->data.blocks[b].sensor[s].sensor_id | d[dnum+1]; + o->data.blocks[b].sensor[s].value = d[dnum+2] << 8; + o->data.blocks[b].sensor[s].value = o->data.blocks[b].sensor[s].value | d[dnum+3]; + printk("sensor[%d]-[%d]: id: %u, value: %u\n", + b, s, o->data.blocks[b].sensor[s].sensor_id, o->data.blocks[b].sensor[s].value); + dnum = dnum + o->data.blocks[b].sensor_length; + } + } + else if (strcmp(o->data.blocks[b].sensor_type, "TEMP") == 0) { + + o->data.blocks[b].sensor = + kzalloc(sizeof(occ_sensor) * o->data.blocks[b].num_of_sensors, GFP_KERNEL); + + if (o->data.blocks[b].sensor == NULL) { + ret = -ENOMEM; + goto abort; + } + + o->temp_block_id = b; + for (s = 0; s < o->data.blocks[b].num_of_sensors; s++) { + o->data.blocks[b].sensor[s].sensor_id = d[dnum] << 8; + o->data.blocks[b].sensor[s].sensor_id = + o->data.blocks[b].sensor[s].sensor_id | d[dnum+1]; + o->data.blocks[b].sensor[s].value = d[dnum+2] << 8; + o->data.blocks[b].sensor[s].value = o->data.blocks[b].sensor[s].value | d[dnum+3]; + printk("sensor[%d]-[%d]: id: %u, value: %u\n", + b, s, o->data.blocks[b].sensor[s].sensor_id, o->data.blocks[b].sensor[s].value); + dnum = dnum + o->data.blocks[b].sensor_length; + } + } + else if (strcmp(o->data.blocks[b].sensor_type, "POWR") == 0) { +/* + o->data.blocks[b].powr = + kzalloc(sizeof(powr_sensor) * o->data.blocks[b].num_of_sensors, GFP_KERNEL); + + if (o->data.blocks[b].powr == NULL) { + ret = -ENOMEM; + goto abort; + } + o->power_block_id = b; + for (s = 0; s< o->data.blocks[b].num_of_sensors; s++) { + o->data.blocks[b].powr[s].sensor_id = d[dnum] << 8; + o->data.blocks[b].powr[s].sensor_id = o->data.blocks[b].powr[s].sensor_id | d[dnum+1]; + o->data.blocks[b].powr[s].update_tag = d[dnum+2] << 24; + o->data.blocks[b].powr[s].update_tag = o->data.blocks[b].powr[s].update_tag | d[dnum+3] << 16; + o->data.blocks[b].powr[s].update_tag = o->data.blocks[b].powr[s].update_tag | d[dnum+4] << 8; + o->data.blocks[b].powr[s].update_tag = o->data.blocks[b].powr[s].update_tag | d[dnum+5]; + o->data.blocks[b].powr[s].accumulator = d[dnum+6] << 24; + o->data.blocks[b].powr[s].accumulator = o->data.blocks[b].powr[s].accumulator | d[dnum+7] << 16; + o->data.blocks[b].powr[s].accumulator = o->data.blocks[b].powr[s].accumulator | d[dnum+8] << 8; + o->data.blocks[b].powr[s].accumulator = o->data.blocks[b].powr[s].accumulator | d[dnum+9]; + o->data.blocks[b].powr[s].value = d[dnum+10] << 8; + o->data.blocks[b].powr[s].value = o->data.blocks[b].powr[s].value | d[dnum+11]; + + printk("sensor[%d]-[%d]: id: %u, value: %u\n", + b, s, o->data.blocks[b].sensor[s].sensor_id, o->data.blocks[b].sensor[s].value); + + dnum = dnum + o->data.blocks[b].sensor_length; + } +*/ + } + else { + printk("ERROR: sensor type %s not supported\n", o->data.blocks[b].sensor_type); + continue; + /* FIX: ignore wrong sensor type? */ + //ret = -1; + //goto abort; + } + } + + return ret; +abort: + deinit_occ_resp_buf(o); + return ret; +} + +char fake_occ_rsp[OCC_DATA_MAX] = { +0x69, 0x00, 0x00, 0x00, 0xa4, 0xc3, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x70, 0x5f, 0x6f, 0x63, 0x63, 0x5f, 0x31, 0x35, 0x30, 0x37, +0x31, 0x36, 0x61, 0x00, 0x00, 0x53, 0x45, 0x4e, 0x53, 0x4f, 0x52, 0x04, 0x01, 0x54, 0x45, 0x4d, +0x50, 0x00, 0x01, 0x04, 0x0a, 0x00 ,0x6a, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6d, 0x00, +0x00,0x00,0x6e,0x00, 0x00,0x00,0x6f,0x00, 0x00,0x00,0x70,0x00, 0x00,0x00,0x71,0x00, +0x00,0x00,0x73,0x00, 0x00,0x00,0x74,0x00, 0x00,0x00,0x75,0x00, 0x00,0x46,0x52,0x45, +0x51,0x00,0x01,0x04, 0x0a,0x00,0x76,0x00, 0x00,0x00,0x78,0x00, 0x00,0x00,0x79,0x00, +0x00,0x00,0x7a,0x00, 0x00,0x00,0x7b,0x00, 0x00,0x00,0x7c,0x00, 0x00,0x00,0x7d,0x00, +0x00,0x00,0x7f,0x00, 0x00,0x00,0x80,0x00, 0x00,0x00,0x81,0x00, 0x00,0x50,0x4f,0x57, +0x52,0x00,0x01,0x0c, 0x00,0x43,0x41,0x50, 0x53,0x00,0x01,0x0c, 0x01,0x00,0x00,0x00, +0x00,0x04,0xb0,0x09, 0x60,0x04,0x4c,0x00, 0x00,0x17,0xc5,}; + +#define DUMP_RAW 1 + +static int occ_get_all(struct i2c_client *client, occ_response_t *occ_resp) +{ + char occ_data[OCC_DATA_MAX]; + uint16_t num_bytes = 0; + int b = 0; + int ret = 0; +/* + //Procedure to access SRAM where OCC data is located + occ_putscom(client, SCOM_OCC_SRAM_WOX, 0x08000000, 0x00000000); + occ_putscom(client, SCOM_OCC_SRAM_WAND, 0xFBFFFFFF, 0xFFFFFFFF); + occ_putscom(client, SCOM_OCC_SRAM_ADDR, OCC_RESPONSE_ADDR, 0x00000000); + occ_putscom(client, SCOM_OCC_SRAM_ADDR, OCC_RESPONSE_ADDR, 0x00000000); + + occ_getscomb(client, SCOM_OCC_SRAM_DATA, occ_data, 0); + +*/ + + // Init OCB + occ_putscom(client, OCB_STATUS_CONTROL_OR, 0x08000000, 0x00000000); + occ_putscom(client, OCB_STATUS_CONTROL_AND, 0xFBFFFFFF, 0xFFFFFFFF); + + // Send poll command to OCC + occ_putscom(client, OCB_ADDRESS, OCC_COMMAND_ADDR, 0x00000000); + occ_putscom(client, OCB_ADDRESS, OCC_COMMAND_ADDR, 0x00000000); + occ_putscom(client, OCB_DATA, 0x00000001, 0x10001100); + + // Trigger ATTN + occ_putscom(client, ATTN_DATA, 0x01010000, 0x00000000); + + // TODO: check command status Refere to "1.6.2 OCC Command/Response Sequence" in OCC_OpenPwr_FW_Interfaces1.2.pdf + // Use sleep as workaround + //msleep(2000); + + // Get response data + occ_putscom(client, OCB_ADDRESS, OCC_RESPONSE_ADDR, 0x00000000); + occ_getscomb(client, OCB_DATA, occ_data, 0); + + + /* FIXME: use fake data to test driver without hw */ + //printk("i2c-occ: using FAKE occ data\n"); + //memcpy(&occ_data[0], &fake_occ_rsp[0], sizeof(occ_data)); + + num_bytes = get_occdata_length(occ_data); + + printk("OCC data length: %d\n", num_bytes); + +#ifdef DUMP_RAW + int i = 0; + printk("\nRAW data\n==================\n"); + for (i = 0; i < 8; i++) { + if(i == 4) printk(" "); + printk("%02x", occ_data[i]); + } + printk("\n"); +#endif + + + + if (num_bytes > OCC_DATA_MAX) { + printk("ERROR: OCC data length must be < 4KB\n"); + /* yi: debug */ + return -1; + } + + if (num_bytes <= 0) { + printk("ERROR: OCC data length is zero\n"); + return -1; + } + + for (b = 8; b < num_bytes; b = b + 8) { + //occ_getscomb(client, SCOM_OCC_SRAM_DATA, occ_data, b); + occ_getscomb(client, OCB_DATA, occ_data, b); +#ifdef DUMP_RAW + for (i = 0; i < 8; i++) { + if(i == 4) printk(" "); + printk("%02x", occ_data[b+i]); + } + printk("\n"); +#endif + + } + + /* FIXME: use fake data to test driver without hw */ + //memcpy(&occ_data[0], &fake_occ_rsp[0], sizeof(occ_data)); + + ret = parse_occ_response(occ_data, occ_resp); + + return ret; +} + + +static int occ_update_device(struct device *dev) +{ + struct occ_drv_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int ret = 0; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + data->sample_time) + || !data->valid) { + dev_dbg(&client->dev, "Starting occ update\n"); + + deinit_occ_resp_buf(data->occ_resp); + + ret = occ_get_all(client, data->occ_resp); + + data->last_updated = jiffies; + data->valid = 1; + } + mutex_unlock(&data->update_lock); + + return ret; +} + +/* ----------------------------------------------------------------------*/ +/* sysfs interface */ + +static int print_occ_resp(char *buf, occ_response_t *p, int index) +{ + int i = 0; + int j = 0; + sensor_data_block *block; + occ_sensor *sensor; + powr_sensor *powr; + + printk("occ hwmon all: index: %d\n", index); + printk("------------- above are debug message, bellow is real output------------\n"); + sprintf(buf, "Dump all sensor data from OCC - Todo\n"); + + return 0; +#if 0 + sprintf(buf, "num_of_sensor_blocks: %u\n", p->data.num_of_sensor_blocks); + for (i = 0; i < p->data.num_of_sensor_blocks; i++) + { + block = &p->data.blocks[i]; + if (block == NULL) + continue; + + snprintf(buf, sizeof(block->sensor_type), "sensor_type: %s\n", block->sensor_type); + sprintf(buf, "num_of_sensors: %u\n", block->num_of_sensors); + sprintf(buf, "sensor_length: %u\n", block->sensor_length); + + if (block->sensor_length == 0) + continue; + + if (strcmp(block->sensor_type, "TEMP") == 0) + { + for (j = 0; j < block->num_of_sensors; j++) + { + sensor = &block->sensor[j]; + if (sensor == NULL) + continue; + + sprintf(buf, "sensor_id: %u\n", sensor->sensor_id); + sprintf(buf, "value: %u\n", sensor->value); + } + } + + + if (strcmp(block->sensor_type, "FREQ") == 0) + { + for (j = 0; j < block->num_of_sensors; j++) + { + sensor = &block->sensor[j]; + if (sensor == NULL) + continue; + + sprintf(buf, "sensor_id: %u\n", sensor->sensor_id); + sprintf(buf, "value: %u\n", sensor->value); + } + + } + + if (strcmp(block->sensor_type, "POWR") == 0) + { + for (j = 0; j < block->num_of_sensors; j++) + { + powr = &block->powr[j]; + if (powr == NULL) + continue; + + sprintf(buf, "sensor_id: %u\n", powr->sensor_id); + sprintf(buf, "value: %u\n", powr->value); + sprintf(buf, "update_tag: %u\n", powr->update_tag); + sprintf(buf, "accumulator: %u\n", powr->accumulator); + } + } + } + return 0; +#endif +} + +/* sysfs attributes for hwmon */ +static ssize_t show_occ_data(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int n = attr->index; + struct occ_drv_data *data = dev_get_drvdata(dev); + int ret = 0; + + ret = occ_update_device(dev); + + if (ret != 0); + { + /* FIXME: to test fake data */ + //printk("ERROR: cannot get occ sensor data\n"); + //return ret; + } + + return print_occ_resp(buf, data->occ_resp, n); +} + +static ssize_t show_occ_temp(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int n = attr->index; + struct occ_drv_data *data = dev_get_drvdata(dev); + int ret = 0; + occ_sensor *sensor; + int val = 0; + + ret = occ_update_device(dev); + + if (ret != 0); + { + /* FIXME: to test fake data */ + //printk("ERROR: cannot get occ sensor data\n"); + //return ret; + } + + printk("temp_block_id: %d, sensor: %d\n", data->occ_resp->temp_block_id, n -1); + sensor = &data->occ_resp->data.blocks[data->occ_resp->temp_block_id].sensor[n - 1]; + val = sensor->value; + printk("temp%d sensor value\n", n, val); + + printk("------------- above are debug message, bellow is real output------------\n"); + return sprintf(buf, "%d\n", val); +} + +static ssize_t show_occ_temp_label(struct device *dev, struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int n = attr->index; + struct occ_drv_data *data = dev_get_drvdata(dev); + int ret = 0; + occ_sensor *sensor; + int val = 0; + + ret = occ_update_device(dev); + + if (ret != 0); + { + /* FIXME: to test fake data */ + //printk("ERROR: cannot get occ sensor data\n"); + //return ret; + } + + sensor = &data->occ_resp->data.blocks[data->occ_resp->temp_block_id].sensor[n - 1]; + val = sensor->sensor_id; + printk("temp%d sensor id\n", n, val); + printk("------------- above are debug message, bellow is real output------------\n"); + + return sprintf(buf, "sensor id: %d\n", val); +} + + +static SENSOR_DEVICE_ATTR(all, S_IRUGO, show_occ_data, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_occ_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_occ_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_occ_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_occ_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_occ_temp, NULL, 5); +static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_occ_temp, NULL, 6); +static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_occ_temp, NULL, 7); +static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_occ_temp, NULL, 8); +static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_occ_temp, NULL, 9); +static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO, show_occ_temp, NULL, 10); +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_occ_temp_label, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_occ_temp_label, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_occ_temp_label, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_occ_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO, show_occ_temp_label, NULL, 5); +static SENSOR_DEVICE_ATTR(temp6_label, S_IRUGO, show_occ_temp_label, NULL, 6); +static SENSOR_DEVICE_ATTR(temp7_label, S_IRUGO, show_occ_temp_label, NULL, 7); +static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_occ_temp_label, NULL, 8); +static SENSOR_DEVICE_ATTR(temp9_label, S_IRUGO, show_occ_temp_label, NULL, 9); +static SENSOR_DEVICE_ATTR(temp10_label, S_IRUGO, show_occ_temp_label, NULL, 10); + +static struct attribute *occ_attrs[] = { + &sensor_dev_attr_all.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp9_input.dev_attr.attr, + &sensor_dev_attr_temp10_input.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp4_label.dev_attr.attr, + &sensor_dev_attr_temp5_label.dev_attr.attr, + &sensor_dev_attr_temp6_label.dev_attr.attr, + &sensor_dev_attr_temp7_label.dev_attr.attr, + &sensor_dev_attr_temp8_label.dev_attr.attr, + &sensor_dev_attr_temp9_label.dev_attr.attr, + &sensor_dev_attr_temp10_label.dev_attr.attr, + + NULL +}; +ATTRIBUTE_GROUPS(occ); + +/*-----------------------------------------------------------------------*/ +/* device probe and removal */ + +#define OCC_I2C_ADDR 0x50 +#define OCC_I2C_NAME "occ-i2c" + +enum occ_type { + occ_id, +}; + + +static int occ_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct occ_drv_data *data; + unsigned long funcs; + + data = devm_kzalloc(dev, sizeof(struct occ_drv_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + data->occ_resp = &occ_resp; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->sample_time = HZ; + + //if (i2cdev_check_addr(client->adapter, OCC_I2C_ADDR)) + // return -EBUSY; + + client->addr = OCC_I2C_ADDR; + + /* configure the driver */ + //dev_dbg(dev, "occ i2c register hwmon\n"); + printk("occ i2c register hwmon\n"); + + data->hwmon_dev = hwmon_device_register_with_groups(dev, "occ", + data, occ_groups); + if (IS_ERR(data->hwmon_dev)) + return PTR_ERR(data->hwmon_dev); + + //dev_info(dev, "%s: sensor '%s'\n", + // dev_name(data->hwmon_dev), client->name); + printk("%s: sensor '%s'\n", + dev_name(data->hwmon_dev), client->name); + + funcs = i2c_get_functionality(client->adapter); + + dev_info(dev, "i2c adaptor supports function: 0x%lx\n", funcs); + + occ_check_i2c_errors(client); + //dev_info(dev, "occ i2c driver ready\n"); + printk("occ i2c driver ready\n"); + + return 0; +} + +static int occ_remove(struct i2c_client *client) +{ + struct occ_drv_data *data = i2c_get_clientdata(client); + + /* free allocated sensor memory */ + deinit_occ_resp_buf(data->occ_resp); + + hwmon_device_unregister(data->hwmon_dev); + return 0; +} + +static const struct i2c_device_id occ_ids[] = { + { OCC_I2C_NAME, occ_id, }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, occ_ids); + +static const struct of_device_id i2c_occ_of_match[] = { + {.compatible = "ibm,occ-i2c"}, + {}, +}; + +MODULE_DEVICE_TABLE(of, i2c_occ_of_match); + +#ifdef CONFIG_PM +static int occ_suspend(struct device *dev) +{ + //struct i2c_client *client = to_i2c_client(dev); + /* TODO */ + return 0; +} + +static int occ_resume(struct device *dev) +{ + //struct i2c_client *client = to_i2c_client(dev); + /* TODO */ + return 0; +} + +static const struct dev_pm_ops occ_dev_pm_ops = { + .suspend = occ_suspend, + .resume = occ_resume, +}; +#define OCC_DEV_PM_OPS (&occ_dev_pm_ops) +#else +#define OCC_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static const unsigned short normal_i2c[] = {0x50, I2C_CLIENT_END }; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int occ_detect(struct i2c_client *new_client, + struct i2c_board_info *info) +{ + strncpy(info->type, OCC_I2C_NAME, sizeof(OCC_I2C_NAME)); + return 0; +} + +static struct i2c_driver occ_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = OCC_I2C_NAME, + .pm = OCC_DEV_PM_OPS, + .of_match_table = i2c_occ_of_match, + }, + .probe = occ_probe, + .remove = occ_remove, + .id_table = occ_ids, + .address_list = normal_i2c, + .detect = occ_detect, +}; + +module_i2c_driver(occ_driver); + +#if 0 + +static struct i2c_board_info my_dev_info[] __initdata = { + { + I2C_BOARD_INFO(OCC_I2C_NAME, 0x50), + }, +}; + +static struct i2c_client *my_client; + +static int occ_init(void) +{ + static int sys_adap_bus_num = 3; + struct i2c_adapter* adap = i2c_get_adapter(sys_adap_bus_num); + + if(adap==NULL) { + printk("[OCC-DEBUG] i2c_get_adapter fail!\n"); + return -1; + } + + my_client = i2c_new_device(adap, &my_dev_info[0]); + if( my_client==NULL ){ + printk("[OCC-DEBUG] i2c_new_device fail!\n"); + return -1; + } + i2c_put_adapter(adap); + return i2c_add_driver(&occ_driver); +} + +static void __exit occ_exit(void) +{ + i2c_unregister_device(my_client); + i2c_del_driver(&occ_driver); +} + +module_init(occ_init); +module_exit(occ_exit); + +#endif + +MODULE_AUTHOR("Li Yi "); +MODULE_DESCRIPTION("BMC OCC monitor driver"); +MODULE_LICENSE("GPL"); diff --git a/meta-openbmc-machines/meta-openpower/i2c-occ-mod/i2c-occ-mod_0.1.bb b/meta-openbmc-machines/meta-openpower/i2c-occ-mod/i2c-occ-mod_0.1.bb new file mode 100644 index 0000000..1f0ea77 --- /dev/null +++ b/meta-openbmc-machines/meta-openpower/i2c-occ-mod/i2c-occ-mod_0.1.bb @@ -0,0 +1,19 @@ +SUMMARY = "build i2c occ hwmon kernel module" +LICENSE = "GPLv2" +LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e" + +inherit module + +#SRC_URI = "file://Makefile \ +# file://occ.c \ +# file://COPYING \ +# " + +SRC_URI += "git://github.com/adamliyi/kernel-hwmon-occ" +#SRC_URI += "git://github.com/openbmc/kernel-hwmon-occ" +SRCREV = "${AUTOREV}" + +S = "${WORKDIR}" + +# The inherit of module.bbclass will automatically name module packages with +# "kernel-module-" prefix as required by the oe-core build environment. -- 2.6.3