Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S964910AbcDTL0z (ORCPT ); Wed, 20 Apr 2016 07:26:55 -0400 Received: from mail-am1on0118.outbound.protection.outlook.com ([157.56.112.118]:51936 "EHLO emea01-am1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S932133AbcDTL0v (ORCPT ); Wed, 20 Apr 2016 07:26:51 -0400 Authentication-Results: linaro.org; dkim=none (message not signed) header.d=none;linaro.org; dmarc=none action=none header.from=gomspace.com; From: Jeppe Ledet-Pedersen To: , , , , , , , , , CC: , , , Jeppe Ledet-Pedersen Subject: [PATCH 1/3] mfd: add Cypress FM33256B Processor Companion driver Date: Wed, 20 Apr 2016 13:07:49 +0200 Message-ID: <1461150471-23163-2-git-send-email-jlp@gomspace.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1461150471-23163-1-git-send-email-jlp@gomspace.com> References: <1461150471-23163-1-git-send-email-jlp@gomspace.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [77.243.61.136] X-ClientProxiedBy: VI1PR02CA0003.eurprd02.prod.outlook.com (10.162.7.141) To DB4PR04MB0637.eurprd04.prod.outlook.com (10.242.221.15) X-MS-Office365-Filtering-Correlation-Id: 6a97f6d4-ffdf-4122-50f0-08d3690c8242 X-Microsoft-Exchange-Diagnostics: 1;DB4PR04MB0637;2:hJZYN2TtcleH5QI4ITt9BwtVuq5gmVGUAh/RrWuGOKScbjbNPWxewdOkTie+QIE/msKKRFVGP3quexkAlJ7jvq3VzlYFKZNCr8Kq0hDRabQm1a86LEWxSmAwlKcs3svBJdGgU6nYHyBLo+4puw4tbfHWOa5TckpN0BxYCWZv6tpfoGxZ6Af3aq8YpqMUXsWK;3:2dUO9jmY4+Dui3J79FejioObgPnZI4YqO5taWokj+eGGup6vXl5/oQ9aGdOYj+Y9R2RkTIR0Bkx3RnElowDYP1hJqZug6ZV08/keieh46qwNGio4w6Mqkm39VwDG+N5b;25:zspYdunU2b7U1FnLM3bqOLVvyNL/jYQQ04eSxctSCMZrnVhC7JIkWBSOT3EGZbbK25LB+EWigUQxaJI1oFkMyX6SF2x3ZhOc6x/vEnbg3Hd6sa27W68+t0/HGqUTQnJFnoQGYni7SgrfVcED4ccYthLAB2UzFTbdr6MVsCp0LpmQd+iRHPuvSfYTdK9L9CIbJCPlPDQ2Fwp+6f9k4xCuC4ZW5IQXjzS9FOXdRbGsKCEZ3nyhJ21gKeRRQOeW2E1lFT03oaIvd67Pje1C3YROiOYQtLc0DNkApzSUPDmnN95cIX4vRfHttVwu2aI5K2FIdROMENTMH6D4frh/g1iIeQ== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:DB4PR04MB0637; X-Microsoft-Exchange-Diagnostics: 1;DB4PR04MB0637;20:GnFmojkWUrWJpUcQiFBuDHBXI7Oo0v/fNy0CnrUBMsiv7IiOJByJfqB9VKEI68QOEyeS+S8fWTQk2t/vopKRgEn7/goxwQs9APTfWAtvHAAzqGe9MbO7l5ZOzdCo3CU7g16YrWGvoO2wn6P8GZ7BJuqXkLe+OQQAphx0uHIlT45WhjfA3oYAs03V6qQb1+FJ7WkI7SBvCuMjjIf7jF7HJ6iH71+rTE7Saky91MVtBaF35i2O5VnyDCqSoksgtFZjIhFWoaqU461F2a0j/Dhkj+oTzu9+4YkV5pMIlKO/99l47un26mAJJFtFwteoc0/BenQLmLT5yzX8p5Vls0nzfAb8SVIF4TZVSNoFdbtKPoZh8UN/Yt5uP741AB9kPoIqAtz+9zbMlDlM5o5/4cEkC8y5WvAiI7M8Au/dT6oLBN1HZ5MvWnIaRPht8nq0ZHK6;4:hv8ENrZabyrAN0tl9WIxGJdULfpPCWcrIUE4Ma71pQ/csGspypvPZq8YHrT/Xae7+uYpRtf/rT++U0KKimc2GpdgeKX4iuiLPIqlWAVpyJWq8KTIoI54sOyHUK2AUvmscNBw+Nvw3GpcrCXzlIE/lKyY6+gRUEkfg89uAs4mhvZaGz8Tym8LP1OiG7/zb/iCvkWDpTbWF742xTExJEvYJgJ34LYqSOiXf0O4oZ0I51MWD3fszQ+IGmhc3P5aDPT+C/3vJfvwkeScB9bWyZsilXJzH+AsserhdA4zGAwrWwsx620a/VZaTWMuqeNvDPPBLOBWCLK06qdbw25H1RusqvFnQwdgRwFXUcHiVKoW2EeBq2NEL3w/9vOKsxrhdtZH4Pgr1OqQC0VoMJMISZePTfDWO7SMak/IAnVulf3B+0CK55JNwcy7jpgPqgk8+jW1fnfwV7ADB36xpg7I/y4P1A== X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(9101521026)(6040130)(601004)(2401047)(5005006)(8121501046)(3002001)(10201501046)(6041046)(6043046);SRVR:DB4PR04MB0637;BCL:0;PCL:0;RULEID:;SRVR:DB4PR04MB0637; X-Forefront-PRVS: 0918748D70 X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10019020)(4630300001)(6069001)(6009001)(4001430100002)(92566002)(36756003)(229853001)(5003940100001)(2950100001)(2201001)(33646002)(19580395003)(1096002)(4326007)(15975445007)(6116002)(77096005)(5001770100001)(50226001)(189998001)(42186005)(19580405001)(50986999)(76176999)(5004730100002)(47776003)(48376002)(2906002)(586003)(86362001)(107886002)(3846002)(5008740100001)(50466002)(81166005)(575784001)(66066001)(2004002)(921003)(83996005)(2101003)(1121003);DIR:OUT;SFP:1102;SCL:1;SRVR:DB4PR04MB0637;H:localhost.localdomain;FPR:;SPF:None;MLV:nov;PTR:InfoNoRecords;LANG:en; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;DB4PR04MB0637;23:nU5a5ZeWbIwDb+W8srM+m/TjmlXF+KKBwY3M8PGDh?= =?us-ascii?Q?TuzxKXm9n8axZKv4L1Q/KHeF92SIyNs2r0rBsgnQvWBPtTOrjtmg2L68+KFo?= =?us-ascii?Q?he9skEXCQhKnoy3ILiE7n70MZSMM155pFymAqpfN08nkekpomXO9m32Yd1Mg?= =?us-ascii?Q?c45c4I/rNFy0rONw2xp14g0Nl80iZVlib0n0sEBJ0SZbIH64/8tVTakQIrWl?= =?us-ascii?Q?g9rrqjs72Yyqf+MU4UTBEmmysaaGjFB0MzvcOjL2FbNbp4xHXbWdMce4sbsD?= =?us-ascii?Q?LlQ6bBPEOA5ZL0CokDyWcXBUbLv+WGC2NdcBGdIPlHlvJfF3SdVETAa2nVI8?= =?us-ascii?Q?SLy17YP5PLPMHsOchtenHlPQ364mOL7jqW17Xg9P9p2uIRGKM7chLT6Facg+?= =?us-ascii?Q?yYl9wWAe/ZtugtwhvofGyUh2z9+ZgLlkYaqdrs7zh1Q5GEDc1uhLJxOIMw1V?= =?us-ascii?Q?qYb18fpaGVkjikFlRBX59ra01zQxv1iPyGOAlcPcKWK2bXYTpIGNn7FMJlSl?= =?us-ascii?Q?XVFZ8QJrHA5EdwXIF9hgXns0fXLSx2ZWNMyUKSqeZ4CAYPjLb331wTGMN/+G?= =?us-ascii?Q?md/UXAZLOuwmi4iB4N4BQLk3HA+sLbg/iTvrWn2brCMI1u0O4DYVCLXDM7/o?= =?us-ascii?Q?SkNnMOgnrrkgVtj08Ej4ivZ9RKpxW0d37dtx1siVgnfKf6EO/O8w/dzjdjS5?= =?us-ascii?Q?laSTvcnUoolyqTsvUZdn+CK4mh15mR5SQOaxEPvSg9sKOtdAgkt6Frvjfu5b?= =?us-ascii?Q?qoIVMaA+yKq7CsrwuMqaYJR11yrb6ofd4XVvES8sgmk1j99AlJbTXoPDR7/4?= =?us-ascii?Q?JQK53ZoGxq7J4MSdYVyu89mb9ebVfkJ/4yKn9lldaRpM7KMY3OYqcQlqUsTo?= =?us-ascii?Q?dttnEBAQDeu0ykkn6WTdth4LRdrMj8pQsUchFABnJ1VWSkjIQch7HwWD8uBs?= =?us-ascii?Q?csG+JWzr1+sBmdEWRdutEmPP1ZCtD0WVsMpfhlHiumkNGzH6v7XiyQpj/ovA?= =?us-ascii?Q?oKAcGwBSXibcIMdGRFm7+xZxyCf9tO9eLs/a7Rn72W2eKYZIeoy1sbrV3iiG?= =?us-ascii?Q?LGupClY9JyNE4KWLODvVZUruQjyDLAdbvcfWk/7Zyc6ZJVd1cOMWKvI+rNd/?= =?us-ascii?Q?pFczifKB5Q=3D?= X-Microsoft-Exchange-Diagnostics: 1;DB4PR04MB0637;5:KqUYSXYMXi1tzgKSksn24PIlmLum7H9F3Hm//xQaYkzVTSKtVoRojJY8+xJzkoOPGw0+EqnAXAzdrV2UG0X04vrg2A/ldWmuK0pjGU2zdGqSOXp5fMFzsJ3waBqh3rAu4CxxOYJ/qDTkDdQdoLBuSw==;24:XNuRMK1cVlmv1VXnO/f5351NOFIpmGRNhn54vCWizZoAtZrpzLp8MbgQakO3fdRTCXqyqvCNgUuqgotlYC/r64RmjsNQ0+VeA15D9XyYjNI= SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: gomspace.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 20 Apr 2016 11:11:22.2623 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB4PR04MB0637 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 17751 Lines: 684 This patch adds support for the Cypress Semiconductor FM33256B processor companion. The device contains a 256 kbit FRAM, an RTC, a supply voltage monitor, and a watchdog timer. Signed-off-by: Jeppe Ledet-Pedersen --- Documentation/devicetree/bindings/mfd/fm33256b.txt | 30 ++ MAINTAINERS | 6 + drivers/mfd/Kconfig | 16 + drivers/mfd/Makefile | 1 + drivers/mfd/fm33256b.c | 488 +++++++++++++++++++++ include/linux/mfd/fm33256b.h | 76 ++++ 6 files changed, 617 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/fm33256b.txt create mode 100644 drivers/mfd/fm33256b.c create mode 100644 include/linux/mfd/fm33256b.h diff --git a/Documentation/devicetree/bindings/mfd/fm33256b.txt b/Documentation/devicetree/bindings/mfd/fm33256b.txt new file mode 100644 index 0000000..6591c94 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/fm33256b.txt @@ -0,0 +1,30 @@ +Device-tree bindings for Cypress Semiconductor FM33256B Processor Companion +--------------------------------------------------------------------------- + +Required properties: +- compatible: must be "cypress,fm33256b". +- reg: SPI chip select +- spi-max-frequency: Max SPI frequency to use (< 16000000) + +Optional properties: +- cypress,charge-enabled: enable trickle charger +- cypress,charge-fast: enable fast (1 mA) charging + +The MFD exposes two subdevices: +- The FRAM: "cypress,fm33256b-fram" +- The RTC: "cypress,fm33256b-rtc" + +Example: + +spi1: spi@f800800 { + status = "okay"; + cs-gpios = <&pioC 25 0>; + + fm33256b@0 { + compatible = "cypress,fm33256b"; + spi-max-frequency = <10000000>; + cypress,charge-enabled; + cypress,charge-fast; + reg = <0>; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 1d5b4be..87b5023 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3382,6 +3382,12 @@ T: git git://linuxtv.org/anttip/media_tree.git S: Maintained F: drivers/media/common/cypress_firmware* +CYPRESS FM33256B PROCESSOR COMPANION DRIVER +M: Jeppe Ledet-Pedersen +S: Maintained +F: include/linux/mfd/fm33256b.h +F: drivers/mfd/fm33256b.c + CYTTSP TOUCHSCREEN DRIVER M: Ferruh Yigit L: linux-input@vger.kernel.org diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index eea61e3..3a0d3a3 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -93,6 +93,22 @@ config MFD_ATMEL_HLCDC additional drivers must be enabled in order to use the functionality of the device. +config MFD_FM33256B + tristate "Cypress FM33256 Processor Companion support" + select MFD_CORE + select REGMAP_SPI + depends on OF + help + If you say yes here you get support for the Cypress FM33256B + Processor Companion device. + + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + + This driver can also be built as a module. If so the module + will be called fm33256b. + config MFD_BCM590XX tristate "Broadcom BCM590xx PMUs" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 5eaa6465d..a13728d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -203,3 +203,4 @@ intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC) += intel_soc_pmic_bxtwc.o obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o obj-$(CONFIG_MFD_MT6397) += mt6397-core.o +obj-$(CONFIG_MFD_FM33256B) += fm33256b.o diff --git a/drivers/mfd/fm33256b.c b/drivers/mfd/fm33256b.c new file mode 100644 index 0000000..880ccdd --- /dev/null +++ b/drivers/mfd/fm33256b.c @@ -0,0 +1,488 @@ +/* + * Cypress FM33256B Processor Companion Driver + * + * Copyright (C) 2016 GomSpace ApS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +static const struct mfd_cell fm33256b_cells[] = { + { + .name = "fm33256b-rtc", + .of_compatible = "cypress,fm33256b-rtc", + }, + { + .name = "fm33256b-fram", + .of_compatible = "cypress,fm33256b-fram", + }, +}; + +static int fm33256b_io(struct spi_device *spi, bool write_enable, + uint8_t *out, uint8_t *in, size_t len) +{ + struct spi_message m; + struct fm33256b *fm33256b = dev_get_drvdata(&spi->dev); + + uint8_t write_out[1] = {FM33256B_OP_WREN}; + + /* Payload transfer */ + struct spi_transfer t = { + .tx_buf = out, + .rx_buf = in, + .len = len, + }; + + mutex_lock(&fm33256b->lock); + + /* CS must go high for the write enable latch to be enabled, + * so we have to split this in two transfers. + */ + if (write_enable) + spi_write(spi, write_out, 1); + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + spi_sync(spi, &m); + + mutex_unlock(&fm33256b->lock); + + return 0; +} + +static int fm33256b_read_status(struct spi_device *spi, uint8_t *status) +{ + int ret; + uint8_t out[2] = {FM33256B_OP_RDSR, 0xff}; + uint8_t in[2]; + + ret = fm33256b_io(spi, false, out, in, 2); + if (ret < 0) + return ret; + + *status = in[1]; + + return 0; +} + +static int fm33256b_write_status(struct spi_device *spi, uint8_t status) +{ + uint8_t out[2] = {FM33256B_OP_WRSR, status}; + uint8_t in[2]; + + return fm33256b_io(spi, true, out, in, 2); +} + +static int fm33256b_write_fram(struct spi_device *spi, uint16_t addr, + const uint8_t *data, size_t len) +{ + int ret; + uint8_t *out = NULL, *in = NULL; + + out = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL); + in = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL); + if (!out || !in) { + ret = -ENOMEM; + goto out; + } + + out[0] = FM33256B_OP_WRITE; + out[1] = (addr >> 8) & 0xff; + out[2] = addr & 0xff; + memcpy(&out[3], data, len); + + ret = fm33256b_io(spi, true, out, in, 3 + len); + +out: + devm_kfree(&spi->dev, out); + devm_kfree(&spi->dev, in); + + return ret; +} + +static int fm33256b_read_fram(struct spi_device *spi, uint16_t addr, + uint8_t *data, size_t len) +{ + int ret; + uint8_t *out = NULL, *in = NULL; + + out = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL); + in = devm_kzalloc(&spi->dev, 3 + len, GFP_KERNEL); + if (!out || !in) { + ret = -ENOMEM; + goto out; + } + + out[0] = FM33256B_OP_READ; + out[1] = (addr >> 8) & 0xff; + out[2] = addr & 0xff; + memset(&out[3], 0xff, len); + + ret = fm33256b_io(spi, false, out, in, 3 + len); + if (ret == 0) + memcpy(data, &in[3], len); + +out: + devm_kfree(&spi->dev, out); + devm_kfree(&spi->dev, in); + + return ret; +} + +static int fm33256b_write_pc(struct spi_device *spi, uint8_t reg, + const uint8_t *data, size_t len) +{ + int ret; + uint8_t *out = NULL, *in = NULL; + + out = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL); + in = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL); + if (!out || !in) { + ret = -ENOMEM; + goto out; + } + + out[0] = FM33256B_OP_WRPC; + out[1] = reg; + memcpy(&out[2], data, len); + + ret = fm33256b_io(spi, true, out, in, 2 + len); + +out: + devm_kfree(&spi->dev, out); + devm_kfree(&spi->dev, in); + + return ret; +} + +static int fm33256b_read_pc(struct spi_device *spi, uint8_t reg, + uint8_t *data, size_t len) +{ + int ret; + uint8_t *out = NULL, *in = NULL; + + out = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL); + in = devm_kzalloc(&spi->dev, 2 + len, GFP_KERNEL); + if (!out || !in) { + ret = -ENOMEM; + goto out; + } + + out[0] = FM33256B_OP_RDPC; + out[1] = reg; + memset(&out[2], 0xff, len); + + ret = fm33256b_io(spi, false, out, in, 2 + len); + if (ret == 0) + memcpy(data, &in[2], len); + +out: + devm_kfree(&spi->dev, out); + devm_kfree(&spi->dev, in); + + return ret; +} + +static int fm33256b_pc_regmap_read(void *context, const void *reg, + size_t reg_size, void *val, + size_t val_size) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + + if (reg_size != 1) + return -ENOTSUPP; + + return fm33256b_read_pc(spi, *(uint8_t *)reg, val, val_size); +} + +static int fm33256b_pc_regmap_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + + const uint8_t *out = data; + const uint8_t *val = &out[1]; + + uint8_t reg = out[0]; + + return fm33256b_write_pc(spi, reg, val, count - sizeof(reg)); +} + +static int fm33256b_fram_regmap_read(void *context, const void *reg, + size_t reg_size, void *val, + size_t val_size) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + + const uint8_t *addrp = reg; + + uint16_t addr = ((uint16_t)addrp[0] << 8) | addrp[1]; + + if (reg_size != 2) + return -ENOTSUPP; + + return fm33256b_read_fram(spi, addr, val, val_size); +} + +static int fm33256b_fram_regmap_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + + const uint8_t *out = data; + const uint8_t *val = &out[2]; + + uint16_t addr = ((uint16_t)out[0] << 8) | out[1]; + + return fm33256b_write_fram(spi, addr, val, count - sizeof(addr)); +} + +static ssize_t fm33256b_bp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + uint8_t status, bp; + + ret = fm33256b_read_status(spi, &status); + if (ret < 0) + return ret; + + bp = (status & 0x0c) >> 2; + + return snprintf(buf, PAGE_SIZE, "%hhu\n", bp); +} + +static ssize_t fm33256b_bp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + uint8_t status, bp; + unsigned long input; + + ret = kstrtoul(buf, 10, &input); + if (ret < 0) + return ret; + + if (input > 3) + return -EINVAL; + + bp = (uint8_t)input << 2; + + ret = fm33256b_read_status(spi, &status); + if (ret < 0) + return ret; + + status = (status & 0xf3) | bp; + + ret = fm33256b_write_status(spi, status); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t fm33256b_serial_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + char serial[9]; + int ret; + + ret = fm33256b_read_pc(spi, FM33256B_SERIAL_BYTE0_REG, serial, 8); + if (ret < 0) + return ret; + + serial[8] = '\0'; + + return snprintf(buf, PAGE_SIZE, "%-8s\n", serial); +} + +static ssize_t fm33256b_serial_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct spi_device *spi = to_spi_device(dev); + char serial[9]; + int ret; + + if (sscanf(buf, "%8s", serial) != 1) + return -EINVAL; + + ret = fm33256b_write_pc(spi, FM33256B_SERIAL_BYTE0_REG, serial, 8); + if (ret < 0) + return ret; + + return count; +} + +DEVICE_ATTR(bp, S_IWUSR | S_IRUGO, + fm33256b_bp_show, fm33256b_bp_store); +DEVICE_ATTR(serial, S_IWUSR | S_IRUGO, + fm33256b_serial_show, fm33256b_serial_store); + +/* Processor Companion Register Map */ +static const struct regmap_config fm33256b_pc_regmap_conf = { + .name = "pc", + .reg_bits = 8, + .val_bits = 8, + .max_register = FM33256B_MAX_REGISTER, +}; + +static struct regmap_bus fm33256b_pc_regmap_bus = { + .write = fm33256b_pc_regmap_write, + .read = fm33256b_pc_regmap_read, +}; + +/* FRAM Register Map */ +static const struct regmap_config fm33256b_fram_regmap_conf = { + .name = "fram", + .reg_bits = 16, + .val_bits = 8, + .max_register = FM33256B_MAX_FRAM, +}; + +static struct regmap_bus fm33256b_fram_regmap_bus = { + .write = fm33256b_fram_regmap_write, + .read = fm33256b_fram_regmap_read, +}; + +static int fm33256b_setup(struct spi_device *spi, struct fm33256b *fm33256b) +{ + int ret; + uint8_t companion_ctl = FM33256B_ALSW, rtc_alarm_ctl = 0; + + /* Setup charger control from DT */ + if (of_get_property(spi->dev.of_node, "cypress,charge-enabled", NULL)) + companion_ctl |= FM33256B_VBC; + + if (of_get_property(spi->dev.of_node, "cypress,charge-fast", NULL)) + companion_ctl |= FM33256B_FC; + + /* Setup charging if enabled */ + ret = regmap_write(fm33256b->regmap_pc, + FM33256B_COMPANION_CONTROL_REG, + companion_ctl); + if (ret < 0) + return ret; + + /* Enable 32 kHz oscillator */ + ret = regmap_write(fm33256b->regmap_pc, + FM33256B_RTC_ALARM_CONTROL_REG, + rtc_alarm_ctl); + if (ret < 0) + return ret; + + return 0; +} + +static int fm33256b_probe(struct spi_device *spi) +{ + int ret; + struct device *dev = &spi->dev; + struct fm33256b *fm33256b; + + fm33256b = devm_kzalloc(dev, sizeof(*fm33256b), GFP_KERNEL); + if (!fm33256b) + return -ENOMEM; + + mutex_init(&fm33256b->lock); + + spi->mode = SPI_MODE_0; + spi->max_speed_hz = spi->max_speed_hz ? : 8000000; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + /* Setup processor companion regmap */ + fm33256b->regmap_pc = + devm_regmap_init(dev, &fm33256b_pc_regmap_bus, + dev, &fm33256b_pc_regmap_conf); + if (IS_ERR(fm33256b->regmap_pc)) + return PTR_ERR(fm33256b->regmap_pc); + + /* Setup FRAM regmap */ + fm33256b->regmap_fram = + devm_regmap_init(dev, &fm33256b_fram_regmap_bus, + dev, &fm33256b_fram_regmap_conf); + if (IS_ERR(fm33256b->regmap_fram)) + return PTR_ERR(fm33256b->regmap_fram); + + dev_set_drvdata(dev, fm33256b); + + ret = fm33256b_setup(spi, fm33256b); + if (ret < 0) + return ret; + + /* Create sysfs entries */ + ret = device_create_file(&spi->dev, &dev_attr_bp); + if (ret < 0) + return ret; + + ret = device_create_file(&spi->dev, &dev_attr_serial); + if (ret < 0) { + device_remove_file(&spi->dev, &dev_attr_bp); + return ret; + } + + return mfd_add_devices(dev, -1, fm33256b_cells, + ARRAY_SIZE(fm33256b_cells), + NULL, 0, NULL); +} + +static int fm33256b_remove(struct spi_device *spi) +{ + mfd_remove_devices(&spi->dev); + device_remove_file(&spi->dev, &dev_attr_serial); + device_remove_file(&spi->dev, &dev_attr_bp); + + return 0; +} + +static const struct of_device_id fm33256b_dt_ids[] = { + { .compatible = "cypress,fm33256b" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, fm33256b_dt_ids); + +static struct spi_driver fm33256b_spi_driver = { + .driver = { + .name = "fm33256b", + .owner = THIS_MODULE, + .of_match_table = fm33256b_dt_ids, + }, + .probe = fm33256b_probe, + .remove = fm33256b_remove, +}; +module_spi_driver(fm33256b_spi_driver); + +MODULE_ALIAS("spi:fm33256b"); +MODULE_DESCRIPTION("Cypress FM33256B Processor Companion Driver"); +MODULE_AUTHOR("Jeppe Ledet-Pedersen "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/fm33256b.h b/include/linux/mfd/fm33256b.h new file mode 100644 index 0000000..cd1e37a --- /dev/null +++ b/include/linux/mfd/fm33256b.h @@ -0,0 +1,76 @@ +/* + * Cypress FM33256B Processor Companion Driver + * + * Copyright (C) 2016 GomSpace ApS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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, see . + */ + +#ifndef __LINUX_MFD_FM33256B_H +#define __LINUX_MFD_FM33256B_H + +#include +#include +#include + +/* Opcodes */ +#define FM33256B_OP_WREN 0x06 +#define FM33256B_OP_WRDI 0x04 +#define FM33256B_OP_RDSR 0x05 +#define FM33256B_OP_WRSR 0x01 +#define FM33256B_OP_READ 0x03 +#define FM33256B_OP_WRITE 0x02 +#define FM33256B_OP_RDPC 0x13 +#define FM33256B_OP_WRPC 0x12 + +/* RTC/Processor Companion Register Map */ +#define FM33256B_ALARM_MONTH 0x1D +#define FM33256B_COMPANION_CONTROL_REG 0x18 +#define FM33256B_SERIAL_BYTE0_REG 0x10 +#define FM33256B_YEARS_REG 0x08 +#define FM33256B_MONTH_REG 0x07 +#define FM33256B_DATE_REG 0x06 +#define FM33256B_DAY_REG 0x05 +#define FM33256B_HOURS_REG 0x04 +#define FM33256B_MINUTES_REG 0x03 +#define FM33256B_SECONDS_REG 0x02 +#define FM33256B_CAL_CONTROL_REG 0x01 +#define FM33256B_RTC_ALARM_CONTROL_REG 0x00 + +/* Companion Control bits */ +#define FM33256B_ALSW BIT(6) +#define FM33256B_VBC BIT(3) +#define FM33256B_FC BIT(2) + +/* RTC/Alarm Control bits */ +#define FM33256B_R BIT(0) +#define FM33256B_W BIT(1) +#define FM33256B_CAL BIT(2) +#define FM33256B_OSCEN BIT(7) + +/* Limits */ +#define FM33256B_MAX_REGISTER FM33256B_ALARM_MONTH +#define FM33256B_MAX_FRAM (32 * 1024) /* 256 kb */ + +/** + * Structure shared by the MFD device and its subdevices. + * + * @regmap: register map used to access registers + */ +struct fm33256b { + struct mutex lock; + struct regmap *regmap_pc; + struct regmap *regmap_fram; +}; + +#endif /* __LINUX_MFD_FM33256B_H */ -- 2.1.4