From 409c69be433b819c924a8d1c457a835bc6d51700 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 10 Dec 2016 11:51:11 +0200 Subject: ASoC: samsung: Remove tests of member address The driver was checking for non-NULL address of struct's members: - s3c_audio_pdata->type (union), - s3c_audio_pdata->type.i2s (embedded struct). This is pointless as these will be always non-NULL. The 's3c_audio_pdata' is always initialized in static memory so it will be zeroed. Additionally the 'type' member was an union with only one member. It is safe to reorganize the structures to get rid of useless union and checks for addresses to fix the coccinelle warning: >> sound/soc/samsung/i2s.c:1270:2-4: ERROR: test of a variable/field address Reported-by: kbuild test robot Signed-off-by: Krzysztof Kozlowski Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Mark Brown --- include/linux/platform_data/asoc-s3c.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h index 15bf56ee8af7..90641a5daaf0 100644 --- a/include/linux/platform_data/asoc-s3c.h +++ b/include/linux/platform_data/asoc-s3c.h @@ -18,7 +18,7 @@ extern void s3c64xx_ac97_setup_gpio(int); -struct samsung_i2s { +struct samsung_i2s_type { /* If the Primary DAI has 5.1 Channels */ #define QUIRK_PRI_6CHAN (1 << 0) /* If the I2S block has a Stereo Overlay Channel */ @@ -47,7 +47,5 @@ struct s3c_audio_pdata { void *dma_capture; void *dma_play_sec; void *dma_capture_mic; - union { - struct samsung_i2s i2s; - } type; + struct samsung_i2s_type type; }; -- cgit v1.2.3 From 8afda8b26d01ee26a60ef2f0284a7f01a5ed96f8 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 28 Nov 2016 15:06:24 +0300 Subject: spi-nor: Add support for Intel SPI serial flash controller Add support for the SPI serial flash host controller found on many Intel CPUs including Baytrail and Braswell. The SPI serial flash controller is used to access BIOS and other platform specific information. By default the driver exposes a single read-only MTD device but with a module parameter "writeable=1" the MTD device can be made read-write which makes it possible to upgrade BIOS directly from Linux. Signed-off-by: Mika Westerberg Acked-by: Cyrille Pitchen Signed-off-by: Lee Jones --- Documentation/mtd/intel-spi.txt | 88 ++++ drivers/mtd/spi-nor/Kconfig | 20 + drivers/mtd/spi-nor/Makefile | 2 + drivers/mtd/spi-nor/intel-spi-platform.c | 57 +++ drivers/mtd/spi-nor/intel-spi.c | 777 +++++++++++++++++++++++++++++++ drivers/mtd/spi-nor/intel-spi.h | 24 + include/linux/platform_data/intel-spi.h | 31 ++ 7 files changed, 999 insertions(+) create mode 100644 Documentation/mtd/intel-spi.txt create mode 100644 drivers/mtd/spi-nor/intel-spi-platform.c create mode 100644 drivers/mtd/spi-nor/intel-spi.c create mode 100644 drivers/mtd/spi-nor/intel-spi.h create mode 100644 include/linux/platform_data/intel-spi.h (limited to 'include/linux/platform_data') diff --git a/Documentation/mtd/intel-spi.txt b/Documentation/mtd/intel-spi.txt new file mode 100644 index 000000000000..bc357729c2cb --- /dev/null +++ b/Documentation/mtd/intel-spi.txt @@ -0,0 +1,88 @@ +Upgrading BIOS using intel-spi +------------------------------ + +Many Intel CPUs like Baytrail and Braswell include SPI serial flash host +controller which is used to hold BIOS and other platform specific data. +Since contents of the SPI serial flash is crucial for machine to function, +it is typically protected by different hardware protection mechanisms to +avoid accidental (or on purpose) overwrite of the content. + +Not all manufacturers protect the SPI serial flash, mainly because it +allows upgrading the BIOS image directly from an OS. + +The intel-spi driver makes it possible to read and write the SPI serial +flash, if certain protection bits are not set and locked. If it finds +any of them set, the whole MTD device is made read-only to prevent +partial overwrites. By default the driver exposes SPI serial flash +contents as read-only but it can be changed from kernel command line, +passing "intel-spi.writeable=1". + +Please keep in mind that overwriting the BIOS image on SPI serial flash +might render the machine unbootable and requires special equipment like +Dediprog to revive. You have been warned! + +Below are the steps how to upgrade MinnowBoard MAX BIOS directly from +Linux. + + 1) Download and extract the latest Minnowboard MAX BIOS SPI image + [1]. At the time writing this the latest image is v92. + + 2) Install mtd-utils package [2]. We need this in order to erase the SPI + serial flash. Distros like Debian and Fedora have this prepackaged with + name "mtd-utils". + + 3) Add "intel-spi.writeable=1" to the kernel command line and reboot + the board (you can also reload the driver passing "writeable=1" as + module parameter to modprobe). + + 4) Once the board is up and running again, find the right MTD partition + (it is named as "BIOS"): + + # cat /proc/mtd + dev: size erasesize name + mtd0: 00800000 00001000 "BIOS" + + So here it will be /dev/mtd0 but it may vary. + + 5) Make backup of the existing image first: + + # dd if=/dev/mtd0ro of=bios.bak + 16384+0 records in + 16384+0 records out + 8388608 bytes (8.4 MB) copied, 10.0269 s, 837 kB/s + + 6) Verify the backup + + # sha1sum /dev/mtd0ro bios.bak + fdbb011920572ca6c991377c4b418a0502668b73 /dev/mtd0ro + fdbb011920572ca6c991377c4b418a0502668b73 bios.bak + + The SHA1 sums must match. Otherwise do not continue any further! + + 7) Erase the SPI serial flash. After this step, do not reboot the + board! Otherwise it will not start anymore. + + # flash_erase /dev/mtd0 0 0 + Erasing 4 Kibyte @ 7ff000 -- 100 % complete + + 8) Once completed without errors you can write the new BIOS image: + + # dd if=MNW2MAX1.X64.0092.R01.1605221712.bin of=/dev/mtd0 + + 9) Verify that the new content of the SPI serial flash matches the new + BIOS image: + + # sha1sum /dev/mtd0ro MNW2MAX1.X64.0092.R01.1605221712.bin + 9b4df9e4be2057fceec3a5529ec3d950836c87a2 /dev/mtd0ro + 9b4df9e4be2057fceec3a5529ec3d950836c87a2 MNW2MAX1.X64.0092.R01.1605221712.bin + + The SHA1 sums should match. + + 10) Now you can reboot your board and observe the new BIOS starting up + properly. + +References +---------- + +[1] https://firmware.intel.com/sites/default/files/MinnowBoard.MAX_.X64.92.R01.zip +[2] http://www.linux-mtd.infradead.org/ diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 4a682ee0f632..02013ffa5231 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -76,4 +76,24 @@ config SPI_NXP_SPIFI Flash. Enable this option if you have a device with a SPIFI controller and want to access the Flash as a mtd device. +config SPI_INTEL_SPI + tristate + +config SPI_INTEL_SPI_PLATFORM + tristate "Intel PCH/PCU SPI flash platform driver" if EXPERT + depends on X86 + select SPI_INTEL_SPI + help + This enables platform support for the Intel PCH/PCU SPI + controller in master mode. This controller is present in modern + Intel hardware and is used to hold BIOS and other persistent + settings. Using this driver it is possible to upgrade BIOS + directly from Linux. + + Say N here unless you know what you are doing. Overwriting the + SPI flash may render the system unbootable. + + To compile this driver as a module, choose M here: the module + will be called intel-spi-platform. + endif # MTD_SPI_NOR diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 121695e83542..1796e8cd6e1a 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -5,3 +5,5 @@ obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o +obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o +obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o diff --git a/drivers/mtd/spi-nor/intel-spi-platform.c b/drivers/mtd/spi-nor/intel-spi-platform.c new file mode 100644 index 000000000000..5c943df9398f --- /dev/null +++ b/drivers/mtd/spi-nor/intel-spi-platform.c @@ -0,0 +1,57 @@ +/* + * Intel PCH/PCU SPI flash platform driver. + * + * Copyright (C) 2016, Intel Corporation + * Author: Mika Westerberg + * + * 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. + */ + +#include +#include +#include + +#include "intel-spi.h" + +static int intel_spi_platform_probe(struct platform_device *pdev) +{ + struct intel_spi_boardinfo *info; + struct intel_spi *ispi; + struct resource *mem; + + info = dev_get_platdata(&pdev->dev); + if (!info) + return -EINVAL; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ispi = intel_spi_probe(&pdev->dev, mem, info); + if (IS_ERR(ispi)) + return PTR_ERR(ispi); + + platform_set_drvdata(pdev, ispi); + return 0; +} + +static int intel_spi_platform_remove(struct platform_device *pdev) +{ + struct intel_spi *ispi = platform_get_drvdata(pdev); + + return intel_spi_remove(ispi); +} + +static struct platform_driver intel_spi_platform_driver = { + .probe = intel_spi_platform_probe, + .remove = intel_spi_platform_remove, + .driver = { + .name = "intel-spi", + }, +}; + +module_platform_driver(intel_spi_platform_driver); + +MODULE_DESCRIPTION("Intel PCH/PCU SPI flash platform driver"); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:intel-spi"); diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c new file mode 100644 index 000000000000..a10f6027b386 --- /dev/null +++ b/drivers/mtd/spi-nor/intel-spi.c @@ -0,0 +1,777 @@ +/* + * Intel PCH/PCU SPI flash driver. + * + * Copyright (C) 2016, Intel Corporation + * Author: Mika Westerberg + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "intel-spi.h" + +/* Offsets are from @ispi->base */ +#define BFPREG 0x00 + +#define HSFSTS_CTL 0x04 +#define HSFSTS_CTL_FSMIE BIT(31) +#define HSFSTS_CTL_FDBC_SHIFT 24 +#define HSFSTS_CTL_FDBC_MASK (0x3f << HSFSTS_CTL_FDBC_SHIFT) + +#define HSFSTS_CTL_FCYCLE_SHIFT 17 +#define HSFSTS_CTL_FCYCLE_MASK (0x0f << HSFSTS_CTL_FCYCLE_SHIFT) +/* HW sequencer opcodes */ +#define HSFSTS_CTL_FCYCLE_READ (0x00 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_WRITE (0x02 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_ERASE (0x03 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_ERASE_64K (0x04 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_RDID (0x06 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_WRSR (0x07 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_RDSR (0x08 << HSFSTS_CTL_FCYCLE_SHIFT) + +#define HSFSTS_CTL_FGO BIT(16) +#define HSFSTS_CTL_FLOCKDN BIT(15) +#define HSFSTS_CTL_FDV BIT(14) +#define HSFSTS_CTL_SCIP BIT(5) +#define HSFSTS_CTL_AEL BIT(2) +#define HSFSTS_CTL_FCERR BIT(1) +#define HSFSTS_CTL_FDONE BIT(0) + +#define FADDR 0x08 +#define DLOCK 0x0c +#define FDATA(n) (0x10 + ((n) * 4)) + +#define FRACC 0x50 + +#define FREG(n) (0x54 + ((n) * 4)) +#define FREG_BASE_MASK 0x3fff +#define FREG_LIMIT_SHIFT 16 +#define FREG_LIMIT_MASK (0x03fff << FREG_LIMIT_SHIFT) + +/* Offset is from @ispi->pregs */ +#define PR(n) ((n) * 4) +#define PR_WPE BIT(31) +#define PR_LIMIT_SHIFT 16 +#define PR_LIMIT_MASK (0x3fff << PR_LIMIT_SHIFT) +#define PR_RPE BIT(15) +#define PR_BASE_MASK 0x3fff +/* Last PR is GPR0 */ +#define PR_NUM (5 + 1) + +/* Offsets are from @ispi->sregs */ +#define SSFSTS_CTL 0x00 +#define SSFSTS_CTL_FSMIE BIT(23) +#define SSFSTS_CTL_DS BIT(22) +#define SSFSTS_CTL_DBC_SHIFT 16 +#define SSFSTS_CTL_SPOP BIT(11) +#define SSFSTS_CTL_ACS BIT(10) +#define SSFSTS_CTL_SCGO BIT(9) +#define SSFSTS_CTL_COP_SHIFT 12 +#define SSFSTS_CTL_FRS BIT(7) +#define SSFSTS_CTL_DOFRS BIT(6) +#define SSFSTS_CTL_AEL BIT(4) +#define SSFSTS_CTL_FCERR BIT(3) +#define SSFSTS_CTL_FDONE BIT(2) +#define SSFSTS_CTL_SCIP BIT(0) + +#define PREOP_OPTYPE 0x04 +#define OPMENU0 0x08 +#define OPMENU1 0x0c + +/* CPU specifics */ +#define BYT_PR 0x74 +#define BYT_SSFSTS_CTL 0x90 +#define BYT_BCR 0xfc +#define BYT_BCR_WPD BIT(0) +#define BYT_FREG_NUM 5 + +#define LPT_PR 0x74 +#define LPT_SSFSTS_CTL 0x90 +#define LPT_FREG_NUM 5 + +#define BXT_PR 0x84 +#define BXT_SSFSTS_CTL 0xa0 +#define BXT_FREG_NUM 12 + +#define INTEL_SPI_TIMEOUT 5000 /* ms */ +#define INTEL_SPI_FIFO_SZ 64 + +/** + * struct intel_spi - Driver private data + * @dev: Device pointer + * @info: Pointer to board specific info + * @nor: SPI NOR layer structure + * @base: Beginning of MMIO space + * @pregs: Start of protection registers + * @sregs: Start of software sequencer registers + * @nregions: Maximum number of regions + * @writeable: Is the chip writeable + * @swseq: Use SW sequencer in register reads/writes + * @erase_64k: 64k erase supported + * @opcodes: Opcodes which are supported. This are programmed by BIOS + * before it locks down the controller. + * @preopcodes: Preopcodes which are supported. + */ +struct intel_spi { + struct device *dev; + const struct intel_spi_boardinfo *info; + struct spi_nor nor; + void __iomem *base; + void __iomem *pregs; + void __iomem *sregs; + size_t nregions; + bool writeable; + bool swseq; + bool erase_64k; + u8 opcodes[8]; + u8 preopcodes[2]; +}; + +static bool writeable; +module_param(writeable, bool, 0); +MODULE_PARM_DESC(writeable, "Enable write access to SPI flash chip (default=0)"); + +static void intel_spi_dump_regs(struct intel_spi *ispi) +{ + u32 value; + int i; + + dev_dbg(ispi->dev, "BFPREG=0x%08x\n", readl(ispi->base + BFPREG)); + + value = readl(ispi->base + HSFSTS_CTL); + dev_dbg(ispi->dev, "HSFSTS_CTL=0x%08x\n", value); + if (value & HSFSTS_CTL_FLOCKDN) + dev_dbg(ispi->dev, "-> Locked\n"); + + dev_dbg(ispi->dev, "FADDR=0x%08x\n", readl(ispi->base + FADDR)); + dev_dbg(ispi->dev, "DLOCK=0x%08x\n", readl(ispi->base + DLOCK)); + + for (i = 0; i < 16; i++) + dev_dbg(ispi->dev, "FDATA(%d)=0x%08x\n", + i, readl(ispi->base + FDATA(i))); + + dev_dbg(ispi->dev, "FRACC=0x%08x\n", readl(ispi->base + FRACC)); + + for (i = 0; i < ispi->nregions; i++) + dev_dbg(ispi->dev, "FREG(%d)=0x%08x\n", i, + readl(ispi->base + FREG(i))); + for (i = 0; i < PR_NUM; i++) + dev_dbg(ispi->dev, "PR(%d)=0x%08x\n", i, + readl(ispi->pregs + PR(i))); + + value = readl(ispi->sregs + SSFSTS_CTL); + dev_dbg(ispi->dev, "SSFSTS_CTL=0x%08x\n", value); + dev_dbg(ispi->dev, "PREOP_OPTYPE=0x%08x\n", + readl(ispi->sregs + PREOP_OPTYPE)); + dev_dbg(ispi->dev, "OPMENU0=0x%08x\n", readl(ispi->sregs + OPMENU0)); + dev_dbg(ispi->dev, "OPMENU1=0x%08x\n", readl(ispi->sregs + OPMENU1)); + + if (ispi->info->type == INTEL_SPI_BYT) + dev_dbg(ispi->dev, "BCR=0x%08x\n", readl(ispi->base + BYT_BCR)); + + dev_dbg(ispi->dev, "Protected regions:\n"); + for (i = 0; i < PR_NUM; i++) { + u32 base, limit; + + value = readl(ispi->pregs + PR(i)); + if (!(value & (PR_WPE | PR_RPE))) + continue; + + limit = (value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT; + base = value & PR_BASE_MASK; + + dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x [%c%c]\n", + i, base << 12, (limit << 12) | 0xfff, + value & PR_WPE ? 'W' : '.', + value & PR_RPE ? 'R' : '.'); + } + + dev_dbg(ispi->dev, "Flash regions:\n"); + for (i = 0; i < ispi->nregions; i++) { + u32 region, base, limit; + + region = readl(ispi->base + FREG(i)); + base = region & FREG_BASE_MASK; + limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT; + + if (base >= limit || (i > 0 && limit == 0)) + dev_dbg(ispi->dev, " %02d disabled\n", i); + else + dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x\n", + i, base << 12, (limit << 12) | 0xfff); + } + + dev_dbg(ispi->dev, "Using %cW sequencer for register access\n", + ispi->swseq ? 'S' : 'H'); +} + +/* Reads max INTEL_SPI_FIFO_SZ bytes from the device fifo */ +static int intel_spi_read_block(struct intel_spi *ispi, void *buf, size_t size) +{ + size_t bytes; + int i = 0; + + if (size > INTEL_SPI_FIFO_SZ) + return -EINVAL; + + while (size > 0) { + bytes = min_t(size_t, size, 4); + memcpy_fromio(buf, ispi->base + FDATA(i), bytes); + size -= bytes; + buf += bytes; + i++; + } + + return 0; +} + +/* Writes max INTEL_SPI_FIFO_SZ bytes to the device fifo */ +static int intel_spi_write_block(struct intel_spi *ispi, const void *buf, + size_t size) +{ + size_t bytes; + int i = 0; + + if (size > INTEL_SPI_FIFO_SZ) + return -EINVAL; + + while (size > 0) { + bytes = min_t(size_t, size, 4); + memcpy_toio(ispi->base + FDATA(i), buf, bytes); + size -= bytes; + buf += bytes; + i++; + } + + return 0; +} + +static int intel_spi_wait_hw_busy(struct intel_spi *ispi) +{ + u32 val; + + return readl_poll_timeout(ispi->base + HSFSTS_CTL, val, + !(val & HSFSTS_CTL_SCIP), 0, + INTEL_SPI_TIMEOUT * 1000); +} + +static int intel_spi_wait_sw_busy(struct intel_spi *ispi) +{ + u32 val; + + return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val, + !(val & SSFSTS_CTL_SCIP), 0, + INTEL_SPI_TIMEOUT * 1000); +} + +static int intel_spi_init(struct intel_spi *ispi) +{ + u32 opmenu0, opmenu1, val; + int i; + + switch (ispi->info->type) { + case INTEL_SPI_BYT: + ispi->sregs = ispi->base + BYT_SSFSTS_CTL; + ispi->pregs = ispi->base + BYT_PR; + ispi->nregions = BYT_FREG_NUM; + + if (writeable) { + /* Disable write protection */ + val = readl(ispi->base + BYT_BCR); + if (!(val & BYT_BCR_WPD)) { + val |= BYT_BCR_WPD; + writel(val, ispi->base + BYT_BCR); + val = readl(ispi->base + BYT_BCR); + } + + ispi->writeable = !!(val & BYT_BCR_WPD); + } + + break; + + case INTEL_SPI_LPT: + ispi->sregs = ispi->base + LPT_SSFSTS_CTL; + ispi->pregs = ispi->base + LPT_PR; + ispi->nregions = LPT_FREG_NUM; + break; + + case INTEL_SPI_BXT: + ispi->sregs = ispi->base + BXT_SSFSTS_CTL; + ispi->pregs = ispi->base + BXT_PR; + ispi->nregions = BXT_FREG_NUM; + ispi->erase_64k = true; + break; + + default: + return -EINVAL; + } + + /* Disable #SMI generation */ + val = readl(ispi->base + HSFSTS_CTL); + val &= ~HSFSTS_CTL_FSMIE; + writel(val, ispi->base + HSFSTS_CTL); + + /* + * BIOS programs allowed opcodes and then locks down the register. + * So read back what opcodes it decided to support. That's the set + * we are going to support as well. + */ + opmenu0 = readl(ispi->sregs + OPMENU0); + opmenu1 = readl(ispi->sregs + OPMENU1); + + /* + * Some controllers can only do basic operations using hardware + * sequencer. All other operations are supposed to be carried out + * using software sequencer. If we find that BIOS has programmed + * opcodes for the software sequencer we use that over the hardware + * sequencer. + */ + if (opmenu0 && opmenu1) { + for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) { + ispi->opcodes[i] = opmenu0 >> i * 8; + ispi->opcodes[i + 4] = opmenu1 >> i * 8; + } + + val = readl(ispi->sregs + PREOP_OPTYPE); + ispi->preopcodes[0] = val; + ispi->preopcodes[1] = val >> 8; + + /* Disable #SMI generation from SW sequencer */ + val = readl(ispi->sregs + SSFSTS_CTL); + val &= ~SSFSTS_CTL_FSMIE; + writel(val, ispi->sregs + SSFSTS_CTL); + + ispi->swseq = true; + } + + intel_spi_dump_regs(ispi); + + return 0; +} + +static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++) + if (ispi->opcodes[i] == opcode) + return i; + return -EINVAL; +} + +static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf, + int len) +{ + u32 val, status; + int ret; + + val = readl(ispi->base + HSFSTS_CTL); + val &= ~(HSFSTS_CTL_FCYCLE_MASK | HSFSTS_CTL_FDBC_MASK); + + switch (opcode) { + case SPINOR_OP_RDID: + val |= HSFSTS_CTL_FCYCLE_RDID; + break; + case SPINOR_OP_WRSR: + val |= HSFSTS_CTL_FCYCLE_WRSR; + break; + case SPINOR_OP_RDSR: + val |= HSFSTS_CTL_FCYCLE_RDSR; + break; + default: + return -EINVAL; + } + + val |= (len - 1) << HSFSTS_CTL_FDBC_SHIFT; + val |= HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE; + val |= HSFSTS_CTL_FGO; + writel(val, ispi->base + HSFSTS_CTL); + + ret = intel_spi_wait_hw_busy(ispi); + if (ret) + return ret; + + status = readl(ispi->base + HSFSTS_CTL); + if (status & HSFSTS_CTL_FCERR) + return -EIO; + else if (status & HSFSTS_CTL_AEL) + return -EACCES; + + return 0; +} + +static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf, + int len) +{ + u32 val, status; + int ret; + + ret = intel_spi_opcode_index(ispi, opcode); + if (ret < 0) + return ret; + + val = (len << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS; + val |= ret << SSFSTS_CTL_COP_SHIFT; + val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE; + val |= SSFSTS_CTL_SCGO; + writel(val, ispi->sregs + SSFSTS_CTL); + + ret = intel_spi_wait_sw_busy(ispi); + if (ret) + return ret; + + status = readl(ispi->base + SSFSTS_CTL); + if (status & SSFSTS_CTL_FCERR) + return -EIO; + else if (status & SSFSTS_CTL_AEL) + return -EACCES; + + return 0; +} + +static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +{ + struct intel_spi *ispi = nor->priv; + int ret; + + /* Address of the first chip */ + writel(0, ispi->base + FADDR); + + if (ispi->swseq) + ret = intel_spi_sw_cycle(ispi, opcode, buf, len); + else + ret = intel_spi_hw_cycle(ispi, opcode, buf, len); + + if (ret) + return ret; + + return intel_spi_read_block(ispi, buf, len); +} + +static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +{ + struct intel_spi *ispi = nor->priv; + int ret; + + /* + * This is handled with atomic operation and preop code in Intel + * controller so skip it here now. + */ + if (opcode == SPINOR_OP_WREN) + return 0; + + writel(0, ispi->base + FADDR); + + /* Write the value beforehand */ + ret = intel_spi_write_block(ispi, buf, len); + if (ret) + return ret; + + if (ispi->swseq) + return intel_spi_sw_cycle(ispi, opcode, buf, len); + return intel_spi_hw_cycle(ispi, opcode, buf, len); +} + +static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len, + u_char *read_buf) +{ + struct intel_spi *ispi = nor->priv; + size_t block_size, retlen = 0; + u32 val, status; + ssize_t ret; + + switch (nor->read_opcode) { + case SPINOR_OP_READ: + case SPINOR_OP_READ_FAST: + break; + default: + return -EINVAL; + } + + while (len > 0) { + block_size = min_t(size_t, len, INTEL_SPI_FIFO_SZ); + + writel(from, ispi->base + FADDR); + + val = readl(ispi->base + HSFSTS_CTL); + val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK); + val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE; + val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT; + val |= HSFSTS_CTL_FCYCLE_READ; + val |= HSFSTS_CTL_FGO; + writel(val, ispi->base + HSFSTS_CTL); + + ret = intel_spi_wait_hw_busy(ispi); + if (ret) + return ret; + + status = readl(ispi->base + HSFSTS_CTL); + if (status & HSFSTS_CTL_FCERR) + ret = -EIO; + else if (status & HSFSTS_CTL_AEL) + ret = -EACCES; + + if (ret < 0) { + dev_err(ispi->dev, "read error: %llx: %#x\n", from, + status); + return ret; + } + + ret = intel_spi_read_block(ispi, read_buf, block_size); + if (ret) + return ret; + + len -= block_size; + from += block_size; + retlen += block_size; + read_buf += block_size; + } + + return retlen; +} + +static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len, + const u_char *write_buf) +{ + struct intel_spi *ispi = nor->priv; + size_t block_size, retlen = 0; + u32 val, status; + ssize_t ret; + + while (len > 0) { + block_size = min_t(size_t, len, INTEL_SPI_FIFO_SZ); + + writel(to, ispi->base + FADDR); + + val = readl(ispi->base + HSFSTS_CTL); + val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK); + val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE; + val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT; + val |= HSFSTS_CTL_FCYCLE_WRITE; + + /* Write enable */ + if (ispi->preopcodes[1] == SPINOR_OP_WREN) + val |= SSFSTS_CTL_SPOP; + val |= SSFSTS_CTL_ACS; + writel(val, ispi->base + HSFSTS_CTL); + + ret = intel_spi_write_block(ispi, write_buf, block_size); + if (ret) { + dev_err(ispi->dev, "failed to write block\n"); + return ret; + } + + /* Start the write now */ + val = readl(ispi->base + HSFSTS_CTL); + writel(val | HSFSTS_CTL_FGO, ispi->base + HSFSTS_CTL); + + ret = intel_spi_wait_hw_busy(ispi); + if (ret) { + dev_err(ispi->dev, "timeout\n"); + return ret; + } + + status = readl(ispi->base + HSFSTS_CTL); + if (status & HSFSTS_CTL_FCERR) + ret = -EIO; + else if (status & HSFSTS_CTL_AEL) + ret = -EACCES; + + if (ret < 0) { + dev_err(ispi->dev, "write error: %llx: %#x\n", to, + status); + return ret; + } + + len -= block_size; + to += block_size; + retlen += block_size; + write_buf += block_size; + } + + return retlen; +} + +static int intel_spi_erase(struct spi_nor *nor, loff_t offs) +{ + size_t erase_size, len = nor->mtd.erasesize; + struct intel_spi *ispi = nor->priv; + u32 val, status, cmd; + int ret; + + /* If the hardware can do 64k erase use that when possible */ + if (len >= SZ_64K && ispi->erase_64k) { + cmd = HSFSTS_CTL_FCYCLE_ERASE_64K; + erase_size = SZ_64K; + } else { + cmd = HSFSTS_CTL_FCYCLE_ERASE; + erase_size = SZ_4K; + } + + while (len > 0) { + writel(offs, ispi->base + FADDR); + + val = readl(ispi->base + HSFSTS_CTL); + val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK); + val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE; + val |= cmd; + val |= HSFSTS_CTL_FGO; + writel(val, ispi->base + HSFSTS_CTL); + + ret = intel_spi_wait_hw_busy(ispi); + if (ret) + return ret; + + status = readl(ispi->base + HSFSTS_CTL); + if (status & HSFSTS_CTL_FCERR) + return -EIO; + else if (status & HSFSTS_CTL_AEL) + return -EACCES; + + offs += erase_size; + len -= erase_size; + } + + return 0; +} + +static bool intel_spi_is_protected(const struct intel_spi *ispi, + unsigned int base, unsigned int limit) +{ + int i; + + for (i = 0; i < PR_NUM; i++) { + u32 pr_base, pr_limit, pr_value; + + pr_value = readl(ispi->pregs + PR(i)); + if (!(pr_value & (PR_WPE | PR_RPE))) + continue; + + pr_limit = (pr_value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT; + pr_base = pr_value & PR_BASE_MASK; + + if (pr_base >= base && pr_limit <= limit) + return true; + } + + return false; +} + +/* + * There will be a single partition holding all enabled flash regions. We + * call this "BIOS". + */ +static void intel_spi_fill_partition(struct intel_spi *ispi, + struct mtd_partition *part) +{ + u64 end; + int i; + + memset(part, 0, sizeof(*part)); + + /* Start from the mandatory descriptor region */ + part->size = 4096; + part->name = "BIOS"; + + /* + * Now try to find where this partition ends based on the flash + * region registers. + */ + for (i = 1; i < ispi->nregions; i++) { + u32 region, base, limit; + + region = readl(ispi->base + FREG(i)); + base = region & FREG_BASE_MASK; + limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT; + + if (base >= limit || limit == 0) + continue; + + /* + * If any of the regions have protection bits set, make the + * whole partition read-only to be on the safe side. + */ + if (intel_spi_is_protected(ispi, base, limit)) + ispi->writeable = 0; + + end = (limit << 12) + 4096; + if (end > part->size) + part->size = end; + } +} + +struct intel_spi *intel_spi_probe(struct device *dev, + struct resource *mem, const struct intel_spi_boardinfo *info) +{ + struct mtd_partition part; + struct intel_spi *ispi; + int ret; + + if (!info || !mem) + return ERR_PTR(-EINVAL); + + ispi = devm_kzalloc(dev, sizeof(*ispi), GFP_KERNEL); + if (!ispi) + return ERR_PTR(-ENOMEM); + + ispi->base = devm_ioremap_resource(dev, mem); + if (IS_ERR(ispi->base)) + return ispi->base; + + ispi->dev = dev; + ispi->info = info; + ispi->writeable = info->writeable; + + ret = intel_spi_init(ispi); + if (ret) + return ERR_PTR(ret); + + ispi->nor.dev = ispi->dev; + ispi->nor.priv = ispi; + ispi->nor.read_reg = intel_spi_read_reg; + ispi->nor.write_reg = intel_spi_write_reg; + ispi->nor.read = intel_spi_read; + ispi->nor.write = intel_spi_write; + ispi->nor.erase = intel_spi_erase; + + ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL); + if (ret) { + dev_info(dev, "failed to locate the chip\n"); + return ERR_PTR(ret); + } + + intel_spi_fill_partition(ispi, &part); + + /* Prevent writes if not explicitly enabled */ + if (!ispi->writeable || !writeable) + ispi->nor.mtd.flags &= ~MTD_WRITEABLE; + + ret = mtd_device_parse_register(&ispi->nor.mtd, NULL, NULL, &part, 1); + if (ret) + return ERR_PTR(ret); + + return ispi; +} +EXPORT_SYMBOL_GPL(intel_spi_probe); + +int intel_spi_remove(struct intel_spi *ispi) +{ + return mtd_device_unregister(&ispi->nor.mtd); +} +EXPORT_SYMBOL_GPL(intel_spi_remove); + +MODULE_DESCRIPTION("Intel PCH/PCU SPI flash core driver"); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/spi-nor/intel-spi.h b/drivers/mtd/spi-nor/intel-spi.h new file mode 100644 index 000000000000..5ab7dc250050 --- /dev/null +++ b/drivers/mtd/spi-nor/intel-spi.h @@ -0,0 +1,24 @@ +/* + * Intel PCH/PCU SPI flash driver. + * + * Copyright (C) 2016, Intel Corporation + * Author: Mika Westerberg + * + * 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. + */ + +#ifndef INTEL_SPI_H +#define INTEL_SPI_H + +#include + +struct intel_spi; +struct resource; + +struct intel_spi *intel_spi_probe(struct device *dev, + struct resource *mem, const struct intel_spi_boardinfo *info); +int intel_spi_remove(struct intel_spi *ispi); + +#endif /* INTEL_SPI_H */ diff --git a/include/linux/platform_data/intel-spi.h b/include/linux/platform_data/intel-spi.h new file mode 100644 index 000000000000..942b0c3f8f08 --- /dev/null +++ b/include/linux/platform_data/intel-spi.h @@ -0,0 +1,31 @@ +/* + * Intel PCH/PCU SPI flash driver. + * + * Copyright (C) 2016, Intel Corporation + * Author: Mika Westerberg + * + * 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. + */ + +#ifndef INTEL_SPI_PDATA_H +#define INTEL_SPI_PDATA_H + +enum intel_spi_type { + INTEL_SPI_BYT = 1, + INTEL_SPI_LPT, + INTEL_SPI_BXT, +}; + +/** + * struct intel_spi_boardinfo - Board specific data for Intel SPI driver + * @type: Type which this controller is compatible with + * @writeable: The chip is writeable + */ +struct intel_spi_boardinfo { + enum intel_spi_type type; + bool writeable; +}; + +#endif /* INTEL_SPI_PDATA_H */ -- cgit v1.2.3 From bb5b06750f1d9b2d632d942d8a72b35a4dd930b9 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 4 Jan 2017 13:56:28 +0530 Subject: gpio: davinci: Remove redundant members davinci_gpio_controller stuct davinci_gpio_controller struct has set_data, in_data, clr_data members that are assigned and never used. Signed-off-by: Keerthy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-davinci.c | 3 --- include/linux/platform_data/gpio-davinci.h | 3 --- 2 files changed, 6 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 9191056548fe..163f81e3db83 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -266,9 +266,6 @@ static int davinci_gpio_probe(struct platform_device *pdev) if (!regs) return -ENXIO; chips[i].regs = regs; - chips[i].set_data = ®s->set_data; - chips[i].clr_data = ®s->clr_data; - chips[i].in_data = ®s->in_data; gpiochip_add_data(&chips[i].chip, &chips[i]); } diff --git a/include/linux/platform_data/gpio-davinci.h b/include/linux/platform_data/gpio-davinci.h index 6ace3fd32b6a..44ca5303849c 100644 --- a/include/linux/platform_data/gpio-davinci.h +++ b/include/linux/platform_data/gpio-davinci.h @@ -33,9 +33,6 @@ struct davinci_gpio_controller { /* Serialize access to GPIO registers */ spinlock_t lock; void __iomem *regs; - void __iomem *set_data; - void __iomem *clr_data; - void __iomem *in_data; int gpio_unbanked; unsigned gpio_irq; }; -- cgit v1.2.3 From cbbd86075a7e32b6e67f77d374b9bb53841ab1c7 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Wed, 11 Jan 2017 17:09:50 +0100 Subject: video: fbdev: imxfb: remove the macros for initializing the DMACR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current definitions of DMACR_HM() and DMACR_TM() are correct only for imx1, they're wrong for imx21. The macros are meant for legacy board files only, they're not applicable for boards using device tree. At the moment, there are no boards using these macros. So it should be safe to drop them rather than making them work for both imx1 and imx21, which would require a change in the platform data struct. Signed-off-by: Martin Kaiser Acked-by: Uwe Kleine-König Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/platform_data/video-imxfb.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/video-imxfb.h b/include/linux/platform_data/video-imxfb.h index 18e908324549..a5c0a71ec914 100644 --- a/include/linux/platform_data/video-imxfb.h +++ b/include/linux/platform_data/video-imxfb.h @@ -47,10 +47,6 @@ #define LSCR1_GRAY2(x) (((x) & 0xf) << 4) #define LSCR1_GRAY1(x) (((x) & 0xf)) -#define DMACR_BURST (1 << 31) -#define DMACR_HM(x) (((x) & 0xf) << 16) -#define DMACR_TM(x) ((x) & 0xf) - struct imx_fb_videomode { struct fb_videomode mode; u32 pcr; -- cgit v1.2.3 From f95bd041203b9f27a14e6ab733b9598049d7ffef Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 13 Jan 2017 11:00:25 +0100 Subject: memory: aemif: allow passing device lookup table as platform data TI aemif driver creates its own subnodes of the device tree in order to guarantee that all child devices are probed after the AEMIF timing parameters are configured. Some devices (e.g. da850) use struct of_dev_auxdata for clock lookup but nodes created from within the aemif driver can't access the lookup table. Create a platform data structure that holds a pointer to of_dev_auxdata so that we can use it with of_platform_populate(). Signed-off-by: Bartosz Golaszewski Acked-by: Sekhar Nori Acked-by: Santosh Shilimkar Signed-off-by: Greg Kroah-Hartman --- drivers/memory/ti-aemif.c | 8 +++++++- include/linux/platform_data/ti-aemif.h | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 include/linux/platform_data/ti-aemif.h (limited to 'include/linux/platform_data') diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c index a579a0f25840..22c1aeeb6421 100644 --- a/drivers/memory/ti-aemif.c +++ b/drivers/memory/ti-aemif.c @@ -20,6 +20,7 @@ #include #include #include +#include #define TA_SHIFT 2 #define RHOLD_SHIFT 4 @@ -335,6 +336,8 @@ static int aemif_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct device_node *child_np; struct aemif_device *aemif; + struct aemif_platform_data *pdata; + struct of_dev_auxdata *dev_lookup; if (np == NULL) return 0; @@ -343,6 +346,9 @@ static int aemif_probe(struct platform_device *pdev) if (!aemif) return -ENOMEM; + pdata = dev_get_platdata(&pdev->dev); + dev_lookup = pdata ? pdata->dev_lookup : NULL; + platform_set_drvdata(pdev, aemif); aemif->clk = devm_clk_get(dev, NULL); @@ -390,7 +396,7 @@ static int aemif_probe(struct platform_device *pdev) * parameters are set. */ for_each_available_child_of_node(np, child_np) { - ret = of_platform_populate(child_np, NULL, NULL, dev); + ret = of_platform_populate(child_np, NULL, dev_lookup, dev); if (ret < 0) goto error; } diff --git a/include/linux/platform_data/ti-aemif.h b/include/linux/platform_data/ti-aemif.h new file mode 100644 index 000000000000..ac72e115093c --- /dev/null +++ b/include/linux/platform_data/ti-aemif.h @@ -0,0 +1,23 @@ +/* + * TI DaVinci AEMIF platform glue. + * + * Copyright (C) 2017 BayLibre SAS + * + * Author: + * Bartosz Golaszewski + * + * 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. + */ + +#ifndef __TI_DAVINCI_AEMIF_DATA_H__ +#define __TI_DAVINCI_AEMIF_DATA_H__ + +#include + +struct aemif_platform_data { + struct of_dev_auxdata *dev_lookup; +}; + +#endif /* __TI_DAVINCI_AEMIF_DATA_H__ */ -- cgit v1.2.3 From 199244d69458770770890f8b5988a1b6cad701ad Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 17 Jan 2017 13:57:31 +0200 Subject: dmaengine: dw: add support of iDMA 32-bit hardware iDMA 32-bit is Intel designed DMA controller that behaves like Synopsys Designware DMA. This patch adds a support of the new Intel hardware. Due to iDMA 32-bit has no autoconfiguration the platform code must provide a platform data to dw_dma_probe(). By default full FIFO (1024 bytes) is assigned to channel 0. Here we slice FIFO on equal parts between channels for iDMA 32-bit case. Signed-off-by: Andy Shevchenko Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 101 +++++++++++++++++++++++++++++++---- include/linux/platform_data/dma-dw.h | 2 + 2 files changed, 94 insertions(+), 9 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 0186d6732294..e500950dad82 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -138,16 +138,32 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) dwc->descs_allocated--; } -static void dwc_initialize(struct dw_dma_chan *dwc) +static void dwc_initialize_chan_idma32(struct dw_dma_chan *dwc) +{ + u32 cfghi = 0; + u32 cfglo = 0; + + /* Set default burst alignment */ + cfglo |= IDMA32C_CFGL_DST_BURST_ALIGN | IDMA32C_CFGL_SRC_BURST_ALIGN; + + /* Low 4 bits of the request lines */ + cfghi |= IDMA32C_CFGH_DST_PER(dwc->dws.dst_id & 0xf); + cfghi |= IDMA32C_CFGH_SRC_PER(dwc->dws.src_id & 0xf); + + /* Request line extension (2 bits) */ + cfghi |= IDMA32C_CFGH_DST_PER_EXT(dwc->dws.dst_id >> 4 & 0x3); + cfghi |= IDMA32C_CFGH_SRC_PER_EXT(dwc->dws.src_id >> 4 & 0x3); + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); +} + +static void dwc_initialize_chan_dw(struct dw_dma_chan *dwc) { - struct dw_dma *dw = to_dw_dma(dwc->chan.device); u32 cfghi = DWC_CFGH_FIFO_MODE; u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); bool hs_polarity = dwc->dws.hs_polarity; - if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags)) - return; - cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id); cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id); @@ -156,6 +172,19 @@ static void dwc_initialize(struct dw_dma_chan *dwc) channel_writel(dwc, CFG_LO, cfglo); channel_writel(dwc, CFG_HI, cfghi); +} + +static void dwc_initialize(struct dw_dma_chan *dwc) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + + if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags)) + return; + + if (dw->pdata->is_idma32) + dwc_initialize_chan_idma32(dwc); + else + dwc_initialize_chan_dw(dwc); /* Enable interrupts */ channel_set_bit(dw, MASK.XFER, dwc->mask); @@ -187,8 +216,13 @@ static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) static u32 bytes2block(struct dw_dma_chan *dwc, size_t bytes, unsigned int width, size_t *len) { + struct dw_dma *dw = to_dw_dma(dwc->chan.device); u32 block; + /* Always in bytes for iDMA 32-bit */ + if (dw->pdata->is_idma32) + width = 0; + if ((bytes >> width) > dwc->block_size) { block = dwc->block_size; *len = block << width; @@ -202,6 +236,11 @@ static u32 bytes2block(struct dw_dma_chan *dwc, size_t bytes, static size_t block2bytes(struct dw_dma_chan *dwc, u32 block, u32 width) { + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + + if (dw->pdata->is_idma32) + return IDMA32C_CTLH_BLOCK_TS(block); + return DWC_CTLH_BLOCK_TS(block) << width; } @@ -915,14 +954,16 @@ static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dma_slave_config *sc = &dwc->dma_sconfig; + struct dw_dma *dw = to_dw_dma(chan->device); /* * Fix sconfig's burst size according to dw_dmac. We need to convert * them as: * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. * * NOTE: burst size 2 is not supported by DesignWare controller. + * iDMA 32-bit supports it. */ - u32 s = 2; + u32 s = dw->pdata->is_idma32 ? 1 : 2; /* Check if chan will be configured for slave transfers */ if (!is_slave_direction(sconfig->direction)) @@ -937,12 +978,19 @@ static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig) return 0; } -static void dwc_chan_pause(struct dw_dma_chan *dwc) +static void dwc_chan_pause(struct dw_dma_chan *dwc, bool drain) { + struct dw_dma *dw = to_dw_dma(dwc->chan.device); unsigned int count = 20; /* timeout iterations */ u32 cfglo; cfglo = channel_readl(dwc, CFG_LO); + if (dw->pdata->is_idma32) { + if (drain) + cfglo |= IDMA32C_CFGL_CH_DRAIN; + else + cfglo &= ~IDMA32C_CFGL_CH_DRAIN; + } channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--) udelay(2); @@ -956,7 +1004,7 @@ static int dwc_pause(struct dma_chan *chan) unsigned long flags; spin_lock_irqsave(&dwc->lock, flags); - dwc_chan_pause(dwc); + dwc_chan_pause(dwc, false); spin_unlock_irqrestore(&dwc->lock, flags); return 0; @@ -998,6 +1046,8 @@ static int dwc_terminate_all(struct dma_chan *chan) clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); + dwc_chan_pause(dwc, true); + dwc_chan_disable(dw, dwc); dwc_chan_resume(dwc); @@ -1090,6 +1140,32 @@ static void dwc_issue_pending(struct dma_chan *chan) /*----------------------------------------------------------------------*/ +/* + * Program FIFO size of channels. + * + * By default full FIFO (1024 bytes) is assigned to channel 0. Here we + * slice FIFO on equal parts between channels. + */ +static void idma32_fifo_partition(struct dw_dma *dw) +{ + u64 value = IDMA32C_FP_PSIZE_CH0(128) | IDMA32C_FP_PSIZE_CH1(128) | + IDMA32C_FP_UPDATE; + u64 fifo_partition = 0; + + if (!dw->pdata->is_idma32) + return; + + /* Fill FIFO_PARTITION low bits (Channels 0..1, 4..5) */ + fifo_partition |= value << 0; + + /* Fill FIFO_PARTITION high bits (Channels 2..3, 6..7) */ + fifo_partition |= value << 32; + + /* Program FIFO Partition registers - 128 bytes for each channel */ + idma32_writeq(dw, FIFO_PARTITION1, fifo_partition); + idma32_writeq(dw, FIFO_PARTITION0, fifo_partition); +} + static void dw_dma_off(struct dw_dma *dw) { unsigned int i; @@ -1509,8 +1585,13 @@ int dw_dma_probe(struct dw_dma_chip *chip) /* Force dma off, just in case */ dw_dma_off(dw); + idma32_fifo_partition(dw); + /* Device and instance ID for IRQ and DMA pool */ - snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", chip->id); + if (pdata->is_idma32) + snprintf(dw->name, sizeof(dw->name), "idma32:dmac%d", chip->id); + else + snprintf(dw->name, sizeof(dw->name), "dw:dmac%d", chip->id); /* Create a pool of consistent memory blocks for hardware descriptors */ dw->desc_pool = dmam_pool_create(dw->name, chip->dev, @@ -1673,6 +1754,8 @@ int dw_dma_enable(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; + idma32_fifo_partition(dw); + dw_dma_on(dw); return 0; } diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index e69e415d0d98..896cb71a382c 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -41,6 +41,7 @@ struct dw_dma_slave { * @is_private: The device channels should be marked as private and not for * by the general purpose DMA channel allocator. * @is_memcpy: The device channels do support memory-to-memory transfers. + * @is_idma32: The type of the DMA controller is iDMA32 * @chan_allocation_order: Allocate channels starting from 0 or 7 * @chan_priority: Set channel priority increasing from 0 to 7 or 7 to 0. * @block_size: Maximum block size supported by the controller @@ -53,6 +54,7 @@ struct dw_dma_platform_data { unsigned int nr_channels; bool is_private; bool is_memcpy; + bool is_idma32; #define CHAN_ALLOCATION_ASCENDING 0 /* zero to seven */ #define CHAN_ALLOCATION_DESCENDING 1 /* seven to zero */ unsigned char chan_allocation_order; -- cgit v1.2.3 From 53d333ac93911dab22d12a78b0a6414f9afb0117 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Tue, 17 Jan 2017 21:49:12 +0530 Subject: gpio: davinci: Remove unwanted blank line Remove redundant blank line. Signed-off-by: Keerthy Reviewed-by: Grygorii Strashko Signed-off-by: Linus Walleij --- include/linux/platform_data/gpio-davinci.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/gpio-davinci.h b/include/linux/platform_data/gpio-davinci.h index 44ca5303849c..18127c4aa4ba 100644 --- a/include/linux/platform_data/gpio-davinci.h +++ b/include/linux/platform_data/gpio-davinci.h @@ -26,7 +26,6 @@ struct davinci_gpio_platform_data { u32 gpio_unbanked; }; - struct davinci_gpio_controller { struct gpio_chip chip; struct irq_domain *irq_domain; -- cgit v1.2.3 From b5cf3fd827d2e11355c126b44ea625650ebf4d39 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Fri, 13 Jan 2017 09:50:12 +0530 Subject: gpio: davinci: Redesign driver to accommodate ngpios in one gpio chip The Davinci GPIO driver is implemented to work with one monolithic Davinci GPIO platform device which may have up to Y(144) gpios. The Davinci GPIO driver instantiates number of GPIO chips with max 32 gpio pins per each during initialization and one IRQ domain. So, the current GPIO's opjects structure is: Davinci GPIO controller |- ------| ... |--- irq_domain (hwirq [0..143]) |- ------| Current driver creates one chip for every 32 GPIOs in a controller. This was a limitation earlier now there is no need for that. Hence redesigning the driver to create one gpio chip for all the ngpio in the controller. |- ------|--- irq_domain (hwirq [0..143]). The previous discussion on this can be found here: https://www.spinics.net/lists/linux-omap/msg132869.html Signed-off-by: Keerthy Signed-off-by: Linus Walleij --- drivers/gpio/gpio-davinci.c | 127 +++++++++++++++++------------ include/linux/platform_data/gpio-davinci.h | 12 ++- 2 files changed, 83 insertions(+), 56 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index bb47de3daafa..446df4ece915 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -63,11 +63,13 @@ static inline int __davinci_direction(struct gpio_chip *chip, unsigned offset, bool out, int value) { struct davinci_gpio_controller *d = gpiochip_get_data(chip); - struct davinci_gpio_regs __iomem *g = d->regs; + struct davinci_gpio_regs __iomem *g; unsigned long flags; u32 temp; - u32 mask = 1 << offset; + int bank = offset / 32; + u32 mask = __gpio_mask(offset); + g = d->regs[bank]; spin_lock_irqsave(&d->lock, flags); temp = readl_relaxed(&g->dir); if (out) { @@ -103,9 +105,12 @@ davinci_direction_out(struct gpio_chip *chip, unsigned offset, int value) static int davinci_gpio_get(struct gpio_chip *chip, unsigned offset) { struct davinci_gpio_controller *d = gpiochip_get_data(chip); - struct davinci_gpio_regs __iomem *g = d->regs; + struct davinci_gpio_regs __iomem *g; + int bank = offset / 32; - return !!((1 << offset) & readl_relaxed(&g->in_data)); + g = d->regs[bank]; + + return !!(__gpio_mask(offset) & readl_relaxed(&g->in_data)); } /* @@ -115,9 +120,13 @@ static void davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct davinci_gpio_controller *d = gpiochip_get_data(chip); - struct davinci_gpio_regs __iomem *g = d->regs; + struct davinci_gpio_regs __iomem *g; + int bank = offset / 32; - writel_relaxed((1 << offset), value ? &g->set_data : &g->clr_data); + g = d->regs[bank]; + + writel_relaxed(__gpio_mask(offset), + value ? &g->set_data : &g->clr_data); } static struct davinci_gpio_platform_data * @@ -165,7 +174,7 @@ static int davinci_gpio_of_xlate(struct gpio_chip *gc, if (gpiospec->args[0] > pdata->ngpio) return -EINVAL; - if (gc != &chips[gpiospec->args[0] / 32].chip) + if (gc != &chips->chip) return -EINVAL; if (flags) @@ -177,11 +186,11 @@ static int davinci_gpio_of_xlate(struct gpio_chip *gc, static int davinci_gpio_probe(struct platform_device *pdev) { - int i, base; + static int ctrl_num; + int gpio, bank; unsigned ngpio, nbank; struct davinci_gpio_controller *chips; struct davinci_gpio_platform_data *pdata; - struct davinci_gpio_regs __iomem *regs; struct device *dev = &pdev->dev; struct resource *res; char label[MAX_LABEL_SIZE]; @@ -220,38 +229,30 @@ static int davinci_gpio_probe(struct platform_device *pdev) if (IS_ERR(gpio_base)) return PTR_ERR(gpio_base); - for (i = 0, base = 0; base < ngpio; i++, base += 32) { - snprintf(label, MAX_LABEL_SIZE, "davinci_gpio.%d", i); - chips[i].chip.label = devm_kstrdup(dev, label, GFP_KERNEL); - if (!chips[i].chip.label) + snprintf(label, MAX_LABEL_SIZE, "davinci_gpio.%d", ctrl_num++); + chips->chip.label = devm_kstrdup(dev, label, GFP_KERNEL); + if (!chips->chip.label) return -ENOMEM; - chips[i].chip.direction_input = davinci_direction_in; - chips[i].chip.get = davinci_gpio_get; - chips[i].chip.direction_output = davinci_direction_out; - chips[i].chip.set = davinci_gpio_set; + chips->chip.direction_input = davinci_direction_in; + chips->chip.get = davinci_gpio_get; + chips->chip.direction_output = davinci_direction_out; + chips->chip.set = davinci_gpio_set; - chips[i].chip.base = base; - chips[i].chip.ngpio = ngpio - base; - if (chips[i].chip.ngpio > 32) - chips[i].chip.ngpio = 32; + chips->chip.ngpio = ngpio; #ifdef CONFIG_OF_GPIO - chips[i].chip.of_gpio_n_cells = 2; - chips[i].chip.of_xlate = davinci_gpio_of_xlate; - chips[i].chip.parent = dev; - chips[i].chip.of_node = dev->of_node; + chips->chip.of_gpio_n_cells = 2; + chips->chip.of_xlate = davinci_gpio_of_xlate; + chips->chip.parent = dev; + chips->chip.of_node = dev->of_node; #endif - spin_lock_init(&chips[i].lock); - - regs = gpio_base + offset_array[i]; - if (!regs) - return -ENXIO; - chips[i].regs = regs; + spin_lock_init(&chips->lock); - gpiochip_add_data(&chips[i].chip, &chips[i]); - } + for (gpio = 0, bank = 0; gpio < ngpio; gpio += 32, bank++) + chips->regs[bank] = gpio_base + offset_array[bank]; + gpiochip_add_data(&chips->chip, chips); platform_set_drvdata(pdev, chips); davinci_gpio_irq_setup(pdev); return 0; @@ -312,16 +313,19 @@ static struct irq_chip gpio_irqchip = { static void gpio_irq_handler(struct irq_desc *desc) { - unsigned int irq = irq_desc_get_irq(desc); struct davinci_gpio_regs __iomem *g; u32 mask = 0xffff; + int bank_num; struct davinci_gpio_controller *d; + struct davinci_gpio_irq_data *irqdata; - d = (struct davinci_gpio_controller *)irq_desc_get_handler_data(desc); - g = (struct davinci_gpio_regs __iomem *)d->regs; + irqdata = (struct davinci_gpio_irq_data *)irq_desc_get_handler_data(desc); + bank_num = irqdata->bank_num; + g = irqdata->regs; + d = irqdata->chip; /* we only care about one bank */ - if (irq & 1) + if ((bank_num % 2) == 1) mask <<= 16; /* temporarily mask (level sensitive) parent IRQ */ @@ -329,6 +333,7 @@ static void gpio_irq_handler(struct irq_desc *desc) while (1) { u32 status; int bit; + irq_hw_number_t hw_irq; /* ack any irqs */ status = readl_relaxed(&g->intstat) & mask; @@ -341,9 +346,13 @@ static void gpio_irq_handler(struct irq_desc *desc) while (status) { bit = __ffs(status); status &= ~BIT(bit); + /* Max number of gpios per controller is 144 so + * hw_irq will be in [0..143] + */ + hw_irq = (bank_num / 2) * 32 + bit; + generic_handle_irq( - irq_find_mapping(d->irq_domain, - d->chip.base + bit)); + irq_find_mapping(d->irq_domain, hw_irq)); } } chained_irq_exit(irq_desc_get_chip(desc), desc); @@ -355,7 +364,7 @@ static int gpio_to_irq_banked(struct gpio_chip *chip, unsigned offset) struct davinci_gpio_controller *d = gpiochip_get_data(chip); if (d->irq_domain) - return irq_create_mapping(d->irq_domain, d->chip.base + offset); + return irq_create_mapping(d->irq_domain, offset); else return -ENXIO; } @@ -369,7 +378,7 @@ static int gpio_to_irq_unbanked(struct gpio_chip *chip, unsigned offset) * can provide direct-mapped IRQs to AINTC (up to 32 GPIOs). */ if (offset < d->gpio_unbanked) - return d->gpio_irq + offset; + return d->base_irq + offset; else return -ENODEV; } @@ -382,7 +391,7 @@ static int gpio_irq_type_unbanked(struct irq_data *data, unsigned trigger) d = (struct davinci_gpio_controller *)irq_data_get_irq_handler_data(data); g = (struct davinci_gpio_regs __iomem *)d->regs; - mask = __gpio_mask(data->irq - d->gpio_irq); + mask = __gpio_mask(data->irq - d->base_irq); if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) return -EINVAL; @@ -401,7 +410,7 @@ davinci_gpio_irq_map(struct irq_domain *d, unsigned int irq, { struct davinci_gpio_controller *chips = (struct davinci_gpio_controller *)d->host_data; - struct davinci_gpio_regs __iomem *g = chips[hw / 32].regs; + struct davinci_gpio_regs __iomem *g = chips->regs[hw / 32]; irq_set_chip_and_handler_name(irq, &gpio_irqchip, handle_simple_irq, "davinci_gpio"); @@ -459,6 +468,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) struct irq_domain *irq_domain = NULL; const struct of_device_id *match; struct irq_chip *irq_chip; + struct davinci_gpio_irq_data *irqdata; gpio_get_irq_chip_cb_t gpio_get_irq_chip; /* @@ -514,10 +524,8 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) * IRQs, while the others use banked IRQs, would need some setup * tweaks to recognize hardware which can do that. */ - for (gpio = 0, bank = 0; gpio < ngpio; bank++, gpio += 32) { - chips[bank].chip.to_irq = gpio_to_irq_banked; - chips[bank].irq_domain = irq_domain; - } + chips->chip.to_irq = gpio_to_irq_banked; + chips->irq_domain = irq_domain; /* * AINTC can handle direct/unbanked IRQs for GPIOs, with the GPIO @@ -526,9 +534,9 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) */ if (pdata->gpio_unbanked) { /* pass "bank 0" GPIO IRQs to AINTC */ - chips[0].chip.to_irq = gpio_to_irq_unbanked; - chips[0].gpio_irq = bank_irq; - chips[0].gpio_unbanked = pdata->gpio_unbanked; + chips->chip.to_irq = gpio_to_irq_unbanked; + chips->base_irq = bank_irq; + chips->gpio_unbanked = pdata->gpio_unbanked; binten = GENMASK(pdata->gpio_unbanked / 16, 0); /* AINTC handles mask/unmask; GPIO handles triggering */ @@ -538,14 +546,14 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) irq_chip->irq_set_type = gpio_irq_type_unbanked; /* default trigger: both edges */ - g = chips[0].regs; + g = chips->regs[0]; writel_relaxed(~0, &g->set_falling); writel_relaxed(~0, &g->set_rising); /* set the direct IRQs up to use that irqchip */ for (gpio = 0; gpio < pdata->gpio_unbanked; gpio++, irq++) { irq_set_chip(irq, irq_chip); - irq_set_handler_data(irq, &chips[gpio / 32]); + irq_set_handler_data(irq, chips); irq_set_status_flags(irq, IRQ_TYPE_EDGE_BOTH); } @@ -561,7 +569,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) * There are register sets for 32 GPIOs. 2 banks of 16 * GPIOs are covered by each set of registers hence divide by 2 */ - g = chips[bank / 2].regs; + g = chips->regs[bank / 2]; writel_relaxed(~0, &g->clr_falling); writel_relaxed(~0, &g->clr_rising); @@ -570,8 +578,19 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) * gpio irqs. Pass the irq bank's corresponding controller to * the chained irq handler. */ + irqdata = devm_kzalloc(&pdev->dev, + sizeof(struct + davinci_gpio_irq_data), + GFP_KERNEL); + if (!irqdata) + return -ENOMEM; + + irqdata->regs = g; + irqdata->bank_num = bank; + irqdata->chip = chips; + irq_set_chained_handler_and_data(bank_irq, gpio_irq_handler, - &chips[gpio / 32]); + irqdata); binten |= BIT(bank); } diff --git a/include/linux/platform_data/gpio-davinci.h b/include/linux/platform_data/gpio-davinci.h index 18127c4aa4ba..c62a9438976d 100644 --- a/include/linux/platform_data/gpio-davinci.h +++ b/include/linux/platform_data/gpio-davinci.h @@ -21,19 +21,27 @@ #include +#define MAX_REGS_BANKS 5 + struct davinci_gpio_platform_data { u32 ngpio; u32 gpio_unbanked; }; +struct davinci_gpio_irq_data { + void __iomem *regs; + struct davinci_gpio_controller *chip; + int bank_num; +}; + struct davinci_gpio_controller { struct gpio_chip chip; struct irq_domain *irq_domain; /* Serialize access to GPIO registers */ spinlock_t lock; - void __iomem *regs; + void __iomem *regs[MAX_REGS_BANKS]; int gpio_unbanked; - unsigned gpio_irq; + unsigned int base_irq; }; /* -- cgit v1.2.3 From 8e11047b8f3cc0dc6df956cf01915077a574168e Mon Sep 17 00:00:00 2001 From: Keerthy Date: Tue, 17 Jan 2017 21:49:14 +0530 Subject: gpio: davinci: Add support for multiple GPIO controllers Update GPIO driver to support Multiple GPIO controllers by updating the base of subsequent GPIO chips with total of previous chips gpio count so that gpio_add_chip gets unique numbers. Signed-off-by: Keerthy Reviewed-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-davinci.c | 4 +++- include/linux/platform_data/gpio-davinci.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux/platform_data') diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 446df4ece915..2f0b6fdbcddb 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -186,7 +186,7 @@ static int davinci_gpio_of_xlate(struct gpio_chip *gc, static int davinci_gpio_probe(struct platform_device *pdev) { - static int ctrl_num; + static int ctrl_num, bank_base; int gpio, bank; unsigned ngpio, nbank; struct davinci_gpio_controller *chips; @@ -240,6 +240,7 @@ static int davinci_gpio_probe(struct platform_device *pdev) chips->chip.set = davinci_gpio_set; chips->chip.ngpio = ngpio; + chips->chip.base = bank_base; #ifdef CONFIG_OF_GPIO chips->chip.of_gpio_n_cells = 2; @@ -248,6 +249,7 @@ static int davinci_gpio_probe(struct platform_device *pdev) chips->chip.of_node = dev->of_node; #endif spin_lock_init(&chips->lock); + bank_base += ngpio; for (gpio = 0, bank = 0; gpio < ngpio; gpio += 32, bank++) chips->regs[bank] = gpio_base + offset_array[bank]; diff --git a/include/linux/platform_data/gpio-davinci.h b/include/linux/platform_data/gpio-davinci.h index c62a9438976d..90ae19ca828f 100644 --- a/include/linux/platform_data/gpio-davinci.h +++ b/include/linux/platform_data/gpio-davinci.h @@ -42,6 +42,7 @@ struct davinci_gpio_controller { void __iomem *regs[MAX_REGS_BANKS]; int gpio_unbanked; unsigned int base_irq; + unsigned int base; }; /* -- cgit v1.2.3 From 1141d9d08184565b71a943a50ffe712b2dfc697f Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Mon, 23 Jan 2017 12:07:42 -0600 Subject: clk: x86: Add Atom PMC platform clocks The BayTrail and CherryTrail platforms provide platform clocks through their Power Management Controller (PMC). The SoC supports up to 6 clocks (PMC_PLT_CLK[0..5]) with a frequency of either 19.2 MHz (PLL) or 25 MHz (XTAL) for BayTrail and a frequency of 19.2 MHz (XTAL) for CherryTrail. These clocks are available for general system use, where appropriate, and each have Control & Frequency register fields associated with them. Port from legacy by Pierre Bossart, integration in clock framework by Irina Tirdea Signed-off-by: Irina Tirdea Signed-off-by: Pierre-Louis Bossart Acked-by: Andy Shevchenko Signed-off-by: Stephen Boyd --- drivers/clk/x86/Makefile | 1 + drivers/clk/x86/clk-pmc-atom.c | 371 +++++++++++++++++++++++++ include/linux/platform_data/x86/clk-pmc-atom.h | 44 +++ 3 files changed, 416 insertions(+) create mode 100644 drivers/clk/x86/clk-pmc-atom.c create mode 100644 include/linux/platform_data/x86/clk-pmc-atom.h (limited to 'include/linux/platform_data') diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile index 04781389d0fb..1367afb03858 100644 --- a/drivers/clk/x86/Makefile +++ b/drivers/clk/x86/Makefile @@ -1,2 +1,3 @@ clk-x86-lpss-objs := clk-lpt.o obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o +obj-$(CONFIG_PMC_ATOM) += clk-pmc-atom.o diff --git a/drivers/clk/x86/clk-pmc-atom.c b/drivers/clk/x86/clk-pmc-atom.c new file mode 100644 index 000000000000..2b60577703ef --- /dev/null +++ b/drivers/clk/x86/clk-pmc-atom.c @@ -0,0 +1,371 @@ +/* + * Intel Atom platform clocks driver for BayTrail and CherryTrail SoCs + * + * Copyright (C) 2016, Intel Corporation + * Author: Irina Tirdea + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include + +#define PLT_CLK_NAME_BASE "pmc_plt_clk" + +#define PMC_CLK_CTL_OFFSET 0x60 +#define PMC_CLK_CTL_SIZE 4 +#define PMC_CLK_NUM 6 +#define PMC_CLK_CTL_GATED_ON_D3 0x0 +#define PMC_CLK_CTL_FORCE_ON 0x1 +#define PMC_CLK_CTL_FORCE_OFF 0x2 +#define PMC_CLK_CTL_RESERVED 0x3 +#define PMC_MASK_CLK_CTL GENMASK(1, 0) +#define PMC_MASK_CLK_FREQ BIT(2) +#define PMC_CLK_FREQ_XTAL (0 << 2) /* 25 MHz */ +#define PMC_CLK_FREQ_PLL (1 << 2) /* 19.2 MHz */ + +struct clk_plt_fixed { + struct clk_hw *clk; + struct clk_lookup *lookup; +}; + +struct clk_plt { + struct clk_hw hw; + void __iomem *reg; + struct clk_lookup *lookup; + /* protect access to PMC registers */ + spinlock_t lock; +}; + +#define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw) + +struct clk_plt_data { + struct clk_plt_fixed **parents; + u8 nparents; + struct clk_plt *clks[PMC_CLK_NUM]; +}; + +/* Return an index in parent table */ +static inline int plt_reg_to_parent(int reg) +{ + switch (reg & PMC_MASK_CLK_FREQ) { + default: + case PMC_CLK_FREQ_XTAL: + return 0; + case PMC_CLK_FREQ_PLL: + return 1; + } +} + +/* Return clk index of parent */ +static inline int plt_parent_to_reg(int index) +{ + switch (index) { + default: + case 0: + return PMC_CLK_FREQ_XTAL; + case 1: + return PMC_CLK_FREQ_PLL; + } +} + +/* Abstract status in simpler enabled/disabled value */ +static inline int plt_reg_to_enabled(int reg) +{ + switch (reg & PMC_MASK_CLK_CTL) { + case PMC_CLK_CTL_GATED_ON_D3: + case PMC_CLK_CTL_FORCE_ON: + return 1; /* enabled */ + case PMC_CLK_CTL_FORCE_OFF: + case PMC_CLK_CTL_RESERVED: + default: + return 0; /* disabled */ + } +} + +static void plt_clk_reg_update(struct clk_plt *clk, u32 mask, u32 val) +{ + u32 tmp; + unsigned long flags; + + spin_lock_irqsave(&clk->lock, flags); + + tmp = readl(clk->reg); + tmp = (tmp & ~mask) | (val & mask); + writel(tmp, clk->reg); + + spin_unlock_irqrestore(&clk->lock, flags); +} + +static int plt_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_plt *clk = to_clk_plt(hw); + + plt_clk_reg_update(clk, PMC_MASK_CLK_FREQ, plt_parent_to_reg(index)); + + return 0; +} + +static u8 plt_clk_get_parent(struct clk_hw *hw) +{ + struct clk_plt *clk = to_clk_plt(hw); + u32 value; + + value = readl(clk->reg); + + return plt_reg_to_parent(value); +} + +static int plt_clk_enable(struct clk_hw *hw) +{ + struct clk_plt *clk = to_clk_plt(hw); + + plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON); + + return 0; +} + +static void plt_clk_disable(struct clk_hw *hw) +{ + struct clk_plt *clk = to_clk_plt(hw); + + plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF); +} + +static int plt_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_plt *clk = to_clk_plt(hw); + u32 value; + + value = readl(clk->reg); + + return plt_reg_to_enabled(value); +} + +static const struct clk_ops plt_clk_ops = { + .enable = plt_clk_enable, + .disable = plt_clk_disable, + .is_enabled = plt_clk_is_enabled, + .get_parent = plt_clk_get_parent, + .set_parent = plt_clk_set_parent, + .determine_rate = __clk_mux_determine_rate, +}; + +static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id, + void __iomem *base, + const char **parent_names, + int num_parents) +{ + struct clk_plt *pclk; + struct clk_init_data init; + int ret; + + pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL); + if (!pclk) + return ERR_PTR(-ENOMEM); + + init.name = kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id); + init.ops = &plt_clk_ops; + init.flags = 0; + init.parent_names = parent_names; + init.num_parents = num_parents; + + pclk->hw.init = &init; + pclk->reg = base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE; + spin_lock_init(&pclk->lock); + + ret = devm_clk_hw_register(&pdev->dev, &pclk->hw); + if (ret) { + pclk = ERR_PTR(ret); + goto err_free_init; + } + + pclk->lookup = clkdev_hw_create(&pclk->hw, init.name, NULL); + if (!pclk->lookup) { + pclk = ERR_PTR(-ENOMEM); + goto err_free_init; + } + +err_free_init: + kfree(init.name); + return pclk; +} + +static void plt_clk_unregister(struct clk_plt *pclk) +{ + clkdev_drop(pclk->lookup); +} + +static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev, + const char *name, + const char *parent_name, + unsigned long fixed_rate) +{ + struct clk_plt_fixed *pclk; + + pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL); + if (!pclk) + return ERR_PTR(-ENOMEM); + + pclk->clk = clk_hw_register_fixed_rate(&pdev->dev, name, parent_name, + 0, fixed_rate); + if (IS_ERR(pclk->clk)) + return ERR_CAST(pclk->clk); + + pclk->lookup = clkdev_hw_create(pclk->clk, name, NULL); + if (!pclk->lookup) { + clk_hw_unregister_fixed_rate(pclk->clk); + return ERR_PTR(-ENOMEM); + } + + return pclk; +} + +static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk) +{ + clkdev_drop(pclk->lookup); + clk_hw_unregister_fixed_rate(pclk->clk); +} + +static void plt_clk_unregister_fixed_rate_loop(struct clk_plt_data *data, + unsigned int i) +{ + while (i--) + plt_clk_unregister_fixed_rate(data->parents[i]); +} + +static void plt_clk_free_parent_names_loop(const char **parent_names, + unsigned int i) +{ + while (i--) + kfree_const(parent_names[i]); + kfree(parent_names); +} + +static void plt_clk_unregister_loop(struct clk_plt_data *data, + unsigned int i) +{ + while (i--) + plt_clk_unregister(data->clks[i]); +} + +static const char **plt_clk_register_parents(struct platform_device *pdev, + struct clk_plt_data *data, + const struct pmc_clk *clks) +{ + const char **parent_names; + unsigned int i; + int err; + int nparents = 0; + + data->nparents = 0; + while (clks[nparents].name) + nparents++; + + data->parents = devm_kcalloc(&pdev->dev, nparents, + sizeof(*data->parents), GFP_KERNEL); + if (!data->parents) + return ERR_PTR(-ENOMEM); + + parent_names = kcalloc(nparents, sizeof(*parent_names), + GFP_KERNEL); + if (!parent_names) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < nparents; i++) { + data->parents[i] = + plt_clk_register_fixed_rate(pdev, clks[i].name, + clks[i].parent_name, + clks[i].freq); + if (IS_ERR(data->parents[i])) { + err = PTR_ERR(data->parents[i]); + goto err_unreg; + } + parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL); + } + + data->nparents = nparents; + return parent_names; + +err_unreg: + plt_clk_unregister_fixed_rate_loop(data, i); + plt_clk_free_parent_names_loop(parent_names, i); + return ERR_PTR(err); +} + +static void plt_clk_unregister_parents(struct clk_plt_data *data) +{ + plt_clk_unregister_fixed_rate_loop(data, data->nparents); +} + +static int plt_clk_probe(struct platform_device *pdev) +{ + const struct pmc_clk_data *pmc_data; + const char **parent_names; + struct clk_plt_data *data; + unsigned int i; + int err; + + pmc_data = dev_get_platdata(&pdev->dev); + if (!pmc_data || !pmc_data->clks) + return -EINVAL; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + parent_names = plt_clk_register_parents(pdev, data, pmc_data->clks); + if (IS_ERR(parent_names)) + return PTR_ERR(parent_names); + + for (i = 0; i < PMC_CLK_NUM; i++) { + data->clks[i] = plt_clk_register(pdev, i, pmc_data->base, + parent_names, data->nparents); + if (IS_ERR(data->clks[i])) { + err = PTR_ERR(data->clks[i]); + goto err_unreg_clk_plt; + } + } + + plt_clk_free_parent_names_loop(parent_names, data->nparents); + + platform_set_drvdata(pdev, data); + return 0; + +err_unreg_clk_plt: + plt_clk_unregister_loop(data, i); + plt_clk_unregister_parents(data); + plt_clk_free_parent_names_loop(parent_names, data->nparents); + return err; +} + +static int plt_clk_remove(struct platform_device *pdev) +{ + struct clk_plt_data *data; + + data = platform_get_drvdata(pdev); + + plt_clk_unregister_loop(data, PMC_CLK_NUM); + plt_clk_unregister_parents(data); + return 0; +} + +static struct platform_driver plt_clk_driver = { + .driver = { + .name = "clk-pmc-atom", + }, + .probe = plt_clk_probe, + .remove = plt_clk_remove, +}; +builtin_platform_driver(plt_clk_driver); diff --git a/include/linux/platform_data/x86/clk-pmc-atom.h b/include/linux/platform_data/x86/clk-pmc-atom.h new file mode 100644 index 000000000000..3ab892208343 --- /dev/null +++ b/include/linux/platform_data/x86/clk-pmc-atom.h @@ -0,0 +1,44 @@ +/* + * Intel Atom platform clocks for BayTrail and CherryTrail SoC. + * + * Copyright (C) 2016, Intel Corporation + * Author: Irina Tirdea + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef __PLATFORM_DATA_X86_CLK_PMC_ATOM_H +#define __PLATFORM_DATA_X86_CLK_PMC_ATOM_H + +/** + * struct pmc_clk - PMC platform clock configuration + * + * @name: identified, typically pmc_plt_clk_, x=[0..5] + * @freq: in Hz, 19.2MHz and 25MHz (Baytrail only) supported + * @parent_name: one of 'xtal' or 'osc' + */ +struct pmc_clk { + const char *name; + unsigned long freq; + const char *parent_name; +}; + +/** + * struct pmc_clk_data - common PMC clock configuration + * + * @base: PMC clock register base offset + * @clks: pointer to set of registered clocks, typically 0..5 + */ +struct pmc_clk_data { + void __iomem *base; + const struct pmc_clk *clks; +}; + +#endif /* __PLATFORM_DATA_X86_CLK_PMC_ATOM_H */ -- cgit v1.2.3 From 80a7581f38c0b2e83dc883a2125340b90b5635ec Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Mon, 23 Jan 2017 12:07:43 -0600 Subject: arch/x86/platform/atom: Move pmc_atom to drivers/platform/x86 The pmc_atom driver does not contain any architecture specific code. It only enables the SoC Power Management Controller driver for BayTrail and CherryTrail platforms. Move the pmc_atom driver from arch/x86/platform/atom to drivers/platform/x86. Also clean-up and reorder include files by alphabetical order in pmc_atom.h Signed-off-by: Irina Tirdea Signed-off-by: Pierre-Louis Bossart Acked-by: Thomas Gleixner Acked-by: Andy Shevchenko Signed-off-by: Stephen Boyd --- arch/x86/Kconfig | 4 - arch/x86/include/asm/pmc_atom.h | 158 ---------- arch/x86/platform/atom/Makefile | 1 - arch/x86/platform/atom/pmc_atom.c | 460 ----------------------------- drivers/acpi/acpi_lpss.c | 2 +- drivers/platform/x86/Kconfig | 4 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/pmc_atom.c | 459 ++++++++++++++++++++++++++++ include/linux/platform_data/x86/pmc_atom.h | 158 ++++++++++ 9 files changed, 623 insertions(+), 624 deletions(-) delete mode 100644 arch/x86/include/asm/pmc_atom.h delete mode 100644 arch/x86/platform/atom/pmc_atom.c create mode 100644 drivers/platform/x86/pmc_atom.c create mode 100644 include/linux/platform_data/x86/pmc_atom.h (limited to 'include/linux/platform_data') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index e487493bbd47..7b4f1789f386 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2789,10 +2789,6 @@ config X86_DMA_REMAP bool depends on STA2X11 -config PMC_ATOM - def_bool y - depends on PCI - source "net/Kconfig" source "drivers/Kconfig" diff --git a/arch/x86/include/asm/pmc_atom.h b/arch/x86/include/asm/pmc_atom.h deleted file mode 100644 index aa8744c77c6d..000000000000 --- a/arch/x86/include/asm/pmc_atom.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Intel Atom SOC Power Management Controller Header File - * Copyright (c) 2014, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - */ - -#ifndef PMC_ATOM_H -#define PMC_ATOM_H - -/* ValleyView Power Control Unit PCI Device ID */ -#define PCI_DEVICE_ID_VLV_PMC 0x0F1C -/* CherryTrail Power Control Unit PCI Device ID */ -#define PCI_DEVICE_ID_CHT_PMC 0x229C - -/* PMC Memory mapped IO registers */ -#define PMC_BASE_ADDR_OFFSET 0x44 -#define PMC_BASE_ADDR_MASK 0xFFFFFE00 -#define PMC_MMIO_REG_LEN 0x100 -#define PMC_REG_BIT_WIDTH 32 - -/* BIOS uses FUNC_DIS to disable specific function */ -#define PMC_FUNC_DIS 0x34 -#define PMC_FUNC_DIS_2 0x38 - -/* CHT specific bits in FUNC_DIS2 register */ -#define BIT_FD_GMM BIT(3) -#define BIT_FD_ISH BIT(4) - -/* S0ix wake event control */ -#define PMC_S0IX_WAKE_EN 0x3C - -#define BIT_LPC_CLOCK_RUN BIT(4) -#define BIT_SHARED_IRQ_GPSC BIT(5) -#define BIT_ORED_DEDICATED_IRQ_GPSS BIT(18) -#define BIT_ORED_DEDICATED_IRQ_GPSC BIT(19) -#define BIT_SHARED_IRQ_GPSS BIT(20) - -#define PMC_WAKE_EN_SETTING ~(BIT_LPC_CLOCK_RUN | \ - BIT_SHARED_IRQ_GPSC | \ - BIT_ORED_DEDICATED_IRQ_GPSS | \ - BIT_ORED_DEDICATED_IRQ_GPSC | \ - BIT_SHARED_IRQ_GPSS) - -/* The timers acumulate time spent in sleep state */ -#define PMC_S0IR_TMR 0x80 -#define PMC_S0I1_TMR 0x84 -#define PMC_S0I2_TMR 0x88 -#define PMC_S0I3_TMR 0x8C -#define PMC_S0_TMR 0x90 -/* Sleep state counter is in units of of 32us */ -#define PMC_TMR_SHIFT 5 - -/* Power status of power islands */ -#define PMC_PSS 0x98 - -#define PMC_PSS_BIT_GBE BIT(0) -#define PMC_PSS_BIT_SATA BIT(1) -#define PMC_PSS_BIT_HDA BIT(2) -#define PMC_PSS_BIT_SEC BIT(3) -#define PMC_PSS_BIT_PCIE BIT(4) -#define PMC_PSS_BIT_LPSS BIT(5) -#define PMC_PSS_BIT_LPE BIT(6) -#define PMC_PSS_BIT_DFX BIT(7) -#define PMC_PSS_BIT_USH_CTRL BIT(8) -#define PMC_PSS_BIT_USH_SUS BIT(9) -#define PMC_PSS_BIT_USH_VCCS BIT(10) -#define PMC_PSS_BIT_USH_VCCA BIT(11) -#define PMC_PSS_BIT_OTG_CTRL BIT(12) -#define PMC_PSS_BIT_OTG_VCCS BIT(13) -#define PMC_PSS_BIT_OTG_VCCA_CLK BIT(14) -#define PMC_PSS_BIT_OTG_VCCA BIT(15) -#define PMC_PSS_BIT_USB BIT(16) -#define PMC_PSS_BIT_USB_SUS BIT(17) - -/* CHT specific bits in PSS register */ -#define PMC_PSS_BIT_CHT_UFS BIT(7) -#define PMC_PSS_BIT_CHT_UXD BIT(11) -#define PMC_PSS_BIT_CHT_UXD_FD BIT(12) -#define PMC_PSS_BIT_CHT_UX_ENG BIT(15) -#define PMC_PSS_BIT_CHT_USB_SUS BIT(16) -#define PMC_PSS_BIT_CHT_GMM BIT(17) -#define PMC_PSS_BIT_CHT_ISH BIT(18) -#define PMC_PSS_BIT_CHT_DFX_MASTER BIT(26) -#define PMC_PSS_BIT_CHT_DFX_CLUSTER1 BIT(27) -#define PMC_PSS_BIT_CHT_DFX_CLUSTER2 BIT(28) -#define PMC_PSS_BIT_CHT_DFX_CLUSTER3 BIT(29) -#define PMC_PSS_BIT_CHT_DFX_CLUSTER4 BIT(30) -#define PMC_PSS_BIT_CHT_DFX_CLUSTER5 BIT(31) - -/* These registers reflect D3 status of functions */ -#define PMC_D3_STS_0 0xA0 - -#define BIT_LPSS1_F0_DMA BIT(0) -#define BIT_LPSS1_F1_PWM1 BIT(1) -#define BIT_LPSS1_F2_PWM2 BIT(2) -#define BIT_LPSS1_F3_HSUART1 BIT(3) -#define BIT_LPSS1_F4_HSUART2 BIT(4) -#define BIT_LPSS1_F5_SPI BIT(5) -#define BIT_LPSS1_F6_XXX BIT(6) -#define BIT_LPSS1_F7_XXX BIT(7) -#define BIT_SCC_EMMC BIT(8) -#define BIT_SCC_SDIO BIT(9) -#define BIT_SCC_SDCARD BIT(10) -#define BIT_SCC_MIPI BIT(11) -#define BIT_HDA BIT(12) -#define BIT_LPE BIT(13) -#define BIT_OTG BIT(14) -#define BIT_USH BIT(15) -#define BIT_GBE BIT(16) -#define BIT_SATA BIT(17) -#define BIT_USB_EHCI BIT(18) -#define BIT_SEC BIT(19) -#define BIT_PCIE_PORT0 BIT(20) -#define BIT_PCIE_PORT1 BIT(21) -#define BIT_PCIE_PORT2 BIT(22) -#define BIT_PCIE_PORT3 BIT(23) -#define BIT_LPSS2_F0_DMA BIT(24) -#define BIT_LPSS2_F1_I2C1 BIT(25) -#define BIT_LPSS2_F2_I2C2 BIT(26) -#define BIT_LPSS2_F3_I2C3 BIT(27) -#define BIT_LPSS2_F4_I2C4 BIT(28) -#define BIT_LPSS2_F5_I2C5 BIT(29) -#define BIT_LPSS2_F6_I2C6 BIT(30) -#define BIT_LPSS2_F7_I2C7 BIT(31) - -#define PMC_D3_STS_1 0xA4 -#define BIT_SMB BIT(0) -#define BIT_OTG_SS_PHY BIT(1) -#define BIT_USH_SS_PHY BIT(2) -#define BIT_DFX BIT(3) - -/* CHT specific bits in PMC_D3_STS_1 register */ -#define BIT_STS_GMM BIT(1) -#define BIT_STS_ISH BIT(2) - -/* PMC I/O Registers */ -#define ACPI_BASE_ADDR_OFFSET 0x40 -#define ACPI_BASE_ADDR_MASK 0xFFFFFE00 -#define ACPI_MMIO_REG_LEN 0x100 - -#define PM1_CNT 0x4 -#define SLEEP_TYPE_MASK 0xFFFFECFF -#define SLEEP_TYPE_S5 0x1C00 -#define SLEEP_ENABLE 0x2000 - -extern int pmc_atom_read(int offset, u32 *value); -extern int pmc_atom_write(int offset, u32 value); - -#endif /* PMC_ATOM_H */ diff --git a/arch/x86/platform/atom/Makefile b/arch/x86/platform/atom/Makefile index 40983f5b0858..57be88fa34bb 100644 --- a/arch/x86/platform/atom/Makefile +++ b/arch/x86/platform/atom/Makefile @@ -1,2 +1 @@ -obj-$(CONFIG_PMC_ATOM) += pmc_atom.o obj-$(CONFIG_PUNIT_ATOM_DEBUG) += punit_atom_debug.o diff --git a/arch/x86/platform/atom/pmc_atom.c b/arch/x86/platform/atom/pmc_atom.c deleted file mode 100644 index 964ff4fc61f9..000000000000 --- a/arch/x86/platform/atom/pmc_atom.c +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Intel Atom SOC Power Management Controller Driver - * Copyright (c) 2014, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include - -struct pmc_bit_map { - const char *name; - u32 bit_mask; -}; - -struct pmc_reg_map { - const struct pmc_bit_map *d3_sts_0; - const struct pmc_bit_map *d3_sts_1; - const struct pmc_bit_map *func_dis; - const struct pmc_bit_map *func_dis_2; - const struct pmc_bit_map *pss; -}; - -struct pmc_dev { - u32 base_addr; - void __iomem *regmap; - const struct pmc_reg_map *map; -#ifdef CONFIG_DEBUG_FS - struct dentry *dbgfs_dir; -#endif /* CONFIG_DEBUG_FS */ - bool init; -}; - -static struct pmc_dev pmc_device; -static u32 acpi_base_addr; - -static const struct pmc_bit_map d3_sts_0_map[] = { - {"LPSS1_F0_DMA", BIT_LPSS1_F0_DMA}, - {"LPSS1_F1_PWM1", BIT_LPSS1_F1_PWM1}, - {"LPSS1_F2_PWM2", BIT_LPSS1_F2_PWM2}, - {"LPSS1_F3_HSUART1", BIT_LPSS1_F3_HSUART1}, - {"LPSS1_F4_HSUART2", BIT_LPSS1_F4_HSUART2}, - {"LPSS1_F5_SPI", BIT_LPSS1_F5_SPI}, - {"LPSS1_F6_Reserved", BIT_LPSS1_F6_XXX}, - {"LPSS1_F7_Reserved", BIT_LPSS1_F7_XXX}, - {"SCC_EMMC", BIT_SCC_EMMC}, - {"SCC_SDIO", BIT_SCC_SDIO}, - {"SCC_SDCARD", BIT_SCC_SDCARD}, - {"SCC_MIPI", BIT_SCC_MIPI}, - {"HDA", BIT_HDA}, - {"LPE", BIT_LPE}, - {"OTG", BIT_OTG}, - {"USH", BIT_USH}, - {"GBE", BIT_GBE}, - {"SATA", BIT_SATA}, - {"USB_EHCI", BIT_USB_EHCI}, - {"SEC", BIT_SEC}, - {"PCIE_PORT0", BIT_PCIE_PORT0}, - {"PCIE_PORT1", BIT_PCIE_PORT1}, - {"PCIE_PORT2", BIT_PCIE_PORT2}, - {"PCIE_PORT3", BIT_PCIE_PORT3}, - {"LPSS2_F0_DMA", BIT_LPSS2_F0_DMA}, - {"LPSS2_F1_I2C1", BIT_LPSS2_F1_I2C1}, - {"LPSS2_F2_I2C2", BIT_LPSS2_F2_I2C2}, - {"LPSS2_F3_I2C3", BIT_LPSS2_F3_I2C3}, - {"LPSS2_F3_I2C4", BIT_LPSS2_F4_I2C4}, - {"LPSS2_F5_I2C5", BIT_LPSS2_F5_I2C5}, - {"LPSS2_F6_I2C6", BIT_LPSS2_F6_I2C6}, - {"LPSS2_F7_I2C7", BIT_LPSS2_F7_I2C7}, - {}, -}; - -static struct pmc_bit_map byt_d3_sts_1_map[] = { - {"SMB", BIT_SMB}, - {"OTG_SS_PHY", BIT_OTG_SS_PHY}, - {"USH_SS_PHY", BIT_USH_SS_PHY}, - {"DFX", BIT_DFX}, - {}, -}; - -static struct pmc_bit_map cht_d3_sts_1_map[] = { - {"SMB", BIT_SMB}, - {"GMM", BIT_STS_GMM}, - {"ISH", BIT_STS_ISH}, - {}, -}; - -static struct pmc_bit_map cht_func_dis_2_map[] = { - {"SMB", BIT_SMB}, - {"GMM", BIT_FD_GMM}, - {"ISH", BIT_FD_ISH}, - {}, -}; - -static const struct pmc_bit_map byt_pss_map[] = { - {"GBE", PMC_PSS_BIT_GBE}, - {"SATA", PMC_PSS_BIT_SATA}, - {"HDA", PMC_PSS_BIT_HDA}, - {"SEC", PMC_PSS_BIT_SEC}, - {"PCIE", PMC_PSS_BIT_PCIE}, - {"LPSS", PMC_PSS_BIT_LPSS}, - {"LPE", PMC_PSS_BIT_LPE}, - {"DFX", PMC_PSS_BIT_DFX}, - {"USH_CTRL", PMC_PSS_BIT_USH_CTRL}, - {"USH_SUS", PMC_PSS_BIT_USH_SUS}, - {"USH_VCCS", PMC_PSS_BIT_USH_VCCS}, - {"USH_VCCA", PMC_PSS_BIT_USH_VCCA}, - {"OTG_CTRL", PMC_PSS_BIT_OTG_CTRL}, - {"OTG_VCCS", PMC_PSS_BIT_OTG_VCCS}, - {"OTG_VCCA_CLK", PMC_PSS_BIT_OTG_VCCA_CLK}, - {"OTG_VCCA", PMC_PSS_BIT_OTG_VCCA}, - {"USB", PMC_PSS_BIT_USB}, - {"USB_SUS", PMC_PSS_BIT_USB_SUS}, - {}, -}; - -static const struct pmc_bit_map cht_pss_map[] = { - {"SATA", PMC_PSS_BIT_SATA}, - {"HDA", PMC_PSS_BIT_HDA}, - {"SEC", PMC_PSS_BIT_SEC}, - {"PCIE", PMC_PSS_BIT_PCIE}, - {"LPSS", PMC_PSS_BIT_LPSS}, - {"LPE", PMC_PSS_BIT_LPE}, - {"UFS", PMC_PSS_BIT_CHT_UFS}, - {"UXD", PMC_PSS_BIT_CHT_UXD}, - {"UXD_FD", PMC_PSS_BIT_CHT_UXD_FD}, - {"UX_ENG", PMC_PSS_BIT_CHT_UX_ENG}, - {"USB_SUS", PMC_PSS_BIT_CHT_USB_SUS}, - {"GMM", PMC_PSS_BIT_CHT_GMM}, - {"ISH", PMC_PSS_BIT_CHT_ISH}, - {"DFX_MASTER", PMC_PSS_BIT_CHT_DFX_MASTER}, - {"DFX_CLUSTER1", PMC_PSS_BIT_CHT_DFX_CLUSTER1}, - {"DFX_CLUSTER2", PMC_PSS_BIT_CHT_DFX_CLUSTER2}, - {"DFX_CLUSTER3", PMC_PSS_BIT_CHT_DFX_CLUSTER3}, - {"DFX_CLUSTER4", PMC_PSS_BIT_CHT_DFX_CLUSTER4}, - {"DFX_CLUSTER5", PMC_PSS_BIT_CHT_DFX_CLUSTER5}, - {}, -}; - -static const struct pmc_reg_map byt_reg_map = { - .d3_sts_0 = d3_sts_0_map, - .d3_sts_1 = byt_d3_sts_1_map, - .func_dis = d3_sts_0_map, - .func_dis_2 = byt_d3_sts_1_map, - .pss = byt_pss_map, -}; - -static const struct pmc_reg_map cht_reg_map = { - .d3_sts_0 = d3_sts_0_map, - .d3_sts_1 = cht_d3_sts_1_map, - .func_dis = d3_sts_0_map, - .func_dis_2 = cht_func_dis_2_map, - .pss = cht_pss_map, -}; - -static inline u32 pmc_reg_read(struct pmc_dev *pmc, int reg_offset) -{ - return readl(pmc->regmap + reg_offset); -} - -static inline void pmc_reg_write(struct pmc_dev *pmc, int reg_offset, u32 val) -{ - writel(val, pmc->regmap + reg_offset); -} - -int pmc_atom_read(int offset, u32 *value) -{ - struct pmc_dev *pmc = &pmc_device; - - if (!pmc->init) - return -ENODEV; - - *value = pmc_reg_read(pmc, offset); - return 0; -} -EXPORT_SYMBOL_GPL(pmc_atom_read); - -int pmc_atom_write(int offset, u32 value) -{ - struct pmc_dev *pmc = &pmc_device; - - if (!pmc->init) - return -ENODEV; - - pmc_reg_write(pmc, offset, value); - return 0; -} -EXPORT_SYMBOL_GPL(pmc_atom_write); - -static void pmc_power_off(void) -{ - u16 pm1_cnt_port; - u32 pm1_cnt_value; - - pr_info("Preparing to enter system sleep state S5\n"); - - pm1_cnt_port = acpi_base_addr + PM1_CNT; - - pm1_cnt_value = inl(pm1_cnt_port); - pm1_cnt_value &= SLEEP_TYPE_MASK; - pm1_cnt_value |= SLEEP_TYPE_S5; - pm1_cnt_value |= SLEEP_ENABLE; - - outl(pm1_cnt_value, pm1_cnt_port); -} - -static void pmc_hw_reg_setup(struct pmc_dev *pmc) -{ - /* - * Disable PMC S0IX_WAKE_EN events coming from: - * - LPC clock run - * - GPIO_SUS ored dedicated IRQs - * - GPIO_SCORE ored dedicated IRQs - * - GPIO_SUS shared IRQ - * - GPIO_SCORE shared IRQ - */ - pmc_reg_write(pmc, PMC_S0IX_WAKE_EN, (u32)PMC_WAKE_EN_SETTING); -} - -#ifdef CONFIG_DEBUG_FS -static void pmc_dev_state_print(struct seq_file *s, int reg_index, - u32 sts, const struct pmc_bit_map *sts_map, - u32 fd, const struct pmc_bit_map *fd_map) -{ - int offset = PMC_REG_BIT_WIDTH * reg_index; - int index; - - for (index = 0; sts_map[index].name; index++) { - seq_printf(s, "Dev: %-2d - %-32s\tState: %s [%s]\n", - offset + index, sts_map[index].name, - fd_map[index].bit_mask & fd ? "Disabled" : "Enabled ", - sts_map[index].bit_mask & sts ? "D3" : "D0"); - } -} - -static int pmc_dev_state_show(struct seq_file *s, void *unused) -{ - struct pmc_dev *pmc = s->private; - const struct pmc_reg_map *m = pmc->map; - u32 func_dis, func_dis_2; - u32 d3_sts_0, d3_sts_1; - - func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS); - func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2); - d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0); - d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1); - - /* Low part */ - pmc_dev_state_print(s, 0, d3_sts_0, m->d3_sts_0, func_dis, m->func_dis); - - /* High part */ - pmc_dev_state_print(s, 1, d3_sts_1, m->d3_sts_1, func_dis_2, m->func_dis_2); - - return 0; -} - -static int pmc_dev_state_open(struct inode *inode, struct file *file) -{ - return single_open(file, pmc_dev_state_show, inode->i_private); -} - -static const struct file_operations pmc_dev_state_ops = { - .open = pmc_dev_state_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int pmc_pss_state_show(struct seq_file *s, void *unused) -{ - struct pmc_dev *pmc = s->private; - const struct pmc_bit_map *map = pmc->map->pss; - u32 pss = pmc_reg_read(pmc, PMC_PSS); - int index; - - for (index = 0; map[index].name; index++) { - seq_printf(s, "Island: %-2d - %-32s\tState: %s\n", - index, map[index].name, - map[index].bit_mask & pss ? "Off" : "On"); - } - return 0; -} - -static int pmc_pss_state_open(struct inode *inode, struct file *file) -{ - return single_open(file, pmc_pss_state_show, inode->i_private); -} - -static const struct file_operations pmc_pss_state_ops = { - .open = pmc_pss_state_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int pmc_sleep_tmr_show(struct seq_file *s, void *unused) -{ - struct pmc_dev *pmc = s->private; - u64 s0ir_tmr, s0i1_tmr, s0i2_tmr, s0i3_tmr, s0_tmr; - - s0ir_tmr = (u64)pmc_reg_read(pmc, PMC_S0IR_TMR) << PMC_TMR_SHIFT; - s0i1_tmr = (u64)pmc_reg_read(pmc, PMC_S0I1_TMR) << PMC_TMR_SHIFT; - s0i2_tmr = (u64)pmc_reg_read(pmc, PMC_S0I2_TMR) << PMC_TMR_SHIFT; - s0i3_tmr = (u64)pmc_reg_read(pmc, PMC_S0I3_TMR) << PMC_TMR_SHIFT; - s0_tmr = (u64)pmc_reg_read(pmc, PMC_S0_TMR) << PMC_TMR_SHIFT; - - seq_printf(s, "S0IR Residency:\t%lldus\n", s0ir_tmr); - seq_printf(s, "S0I1 Residency:\t%lldus\n", s0i1_tmr); - seq_printf(s, "S0I2 Residency:\t%lldus\n", s0i2_tmr); - seq_printf(s, "S0I3 Residency:\t%lldus\n", s0i3_tmr); - seq_printf(s, "S0 Residency:\t%lldus\n", s0_tmr); - return 0; -} - -static int pmc_sleep_tmr_open(struct inode *inode, struct file *file) -{ - return single_open(file, pmc_sleep_tmr_show, inode->i_private); -} - -static const struct file_operations pmc_sleep_tmr_ops = { - .open = pmc_sleep_tmr_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void pmc_dbgfs_unregister(struct pmc_dev *pmc) -{ - debugfs_remove_recursive(pmc->dbgfs_dir); -} - -static int pmc_dbgfs_register(struct pmc_dev *pmc) -{ - struct dentry *dir, *f; - - dir = debugfs_create_dir("pmc_atom", NULL); - if (!dir) - return -ENOMEM; - - pmc->dbgfs_dir = dir; - - f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO, - dir, pmc, &pmc_dev_state_ops); - if (!f) - goto err; - - f = debugfs_create_file("pss_state", S_IFREG | S_IRUGO, - dir, pmc, &pmc_pss_state_ops); - if (!f) - goto err; - - f = debugfs_create_file("sleep_state", S_IFREG | S_IRUGO, - dir, pmc, &pmc_sleep_tmr_ops); - if (!f) - goto err; - - return 0; -err: - pmc_dbgfs_unregister(pmc); - return -ENODEV; -} -#else -static int pmc_dbgfs_register(struct pmc_dev *pmc) -{ - return 0; -} -#endif /* CONFIG_DEBUG_FS */ - -static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - struct pmc_dev *pmc = &pmc_device; - const struct pmc_reg_map *map = (struct pmc_reg_map *)ent->driver_data; - int ret; - - /* Obtain ACPI base address */ - pci_read_config_dword(pdev, ACPI_BASE_ADDR_OFFSET, &acpi_base_addr); - acpi_base_addr &= ACPI_BASE_ADDR_MASK; - - /* Install power off function */ - if (acpi_base_addr != 0 && pm_power_off == NULL) - pm_power_off = pmc_power_off; - - pci_read_config_dword(pdev, PMC_BASE_ADDR_OFFSET, &pmc->base_addr); - pmc->base_addr &= PMC_BASE_ADDR_MASK; - - pmc->regmap = ioremap_nocache(pmc->base_addr, PMC_MMIO_REG_LEN); - if (!pmc->regmap) { - dev_err(&pdev->dev, "error: ioremap failed\n"); - return -ENOMEM; - } - - pmc->map = map; - - /* PMC hardware registers setup */ - pmc_hw_reg_setup(pmc); - - ret = pmc_dbgfs_register(pmc); - if (ret) - dev_warn(&pdev->dev, "debugfs register failed\n"); - - pmc->init = true; - return ret; -} - -/* - * Data for PCI driver interface - * - * used by pci_match_id() call below. - */ -static const struct pci_device_id pmc_pci_ids[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), (kernel_ulong_t)&byt_reg_map }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), (kernel_ulong_t)&cht_reg_map }, - { 0, }, -}; - -static int __init pmc_atom_init(void) -{ - struct pci_dev *pdev = NULL; - const struct pci_device_id *ent; - - /* We look for our device - PCU PMC - * we assume that there is max. one device. - * - * We can't use plain pci_driver mechanism, - * as the device is really a multiple function device, - * main driver that binds to the pci_device is lpc_ich - * and have to find & bind to the device this way. - */ - for_each_pci_dev(pdev) { - ent = pci_match_id(pmc_pci_ids, pdev); - if (ent) - return pmc_setup_dev(pdev, ent); - } - /* Device not found. */ - return -ENODEV; -} - -device_initcall(pmc_atom_init); - -/* -MODULE_AUTHOR("Aubrey Li "); -MODULE_DESCRIPTION("Intel Atom SOC Power Management Controller Interface"); -MODULE_LICENSE("GPL v2"); -*/ diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 8ea836c046f8..90d112a3063a 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,6 @@ ACPI_MODULE_NAME("acpi_lpss"); #include #include #include -#include #define LPSS_ADDR(desc) ((unsigned long)&desc) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 5fe8be089b8b..659b33fc2e18 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1077,3 +1077,7 @@ config MLX_CPLD_PLATFORM cables and fans on the wide range Mellanox IB and Ethernet systems. endif # X86_PLATFORM_DEVICES + +config PMC_ATOM + def_bool y + depends on PCI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index d4111f0f8a78..49ee7ef283bb 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -74,5 +74,6 @@ obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ intel_telemetry_pltdrv.o \ intel_telemetry_debugfs.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o +obj-$(CONFIG_PMC_ATOM) += pmc_atom.o obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_MLX_CPLD_PLATFORM) += mlxcpld-hotplug.o diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c new file mode 100644 index 000000000000..e1dfb1b3632f --- /dev/null +++ b/drivers/platform/x86/pmc_atom.c @@ -0,0 +1,459 @@ +/* + * Intel Atom SOC Power Management Controller Driver + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +struct pmc_bit_map { + const char *name; + u32 bit_mask; +}; + +struct pmc_reg_map { + const struct pmc_bit_map *d3_sts_0; + const struct pmc_bit_map *d3_sts_1; + const struct pmc_bit_map *func_dis; + const struct pmc_bit_map *func_dis_2; + const struct pmc_bit_map *pss; +}; + +struct pmc_dev { + u32 base_addr; + void __iomem *regmap; + const struct pmc_reg_map *map; +#ifdef CONFIG_DEBUG_FS + struct dentry *dbgfs_dir; +#endif /* CONFIG_DEBUG_FS */ + bool init; +}; + +static struct pmc_dev pmc_device; +static u32 acpi_base_addr; + +static const struct pmc_bit_map d3_sts_0_map[] = { + {"LPSS1_F0_DMA", BIT_LPSS1_F0_DMA}, + {"LPSS1_F1_PWM1", BIT_LPSS1_F1_PWM1}, + {"LPSS1_F2_PWM2", BIT_LPSS1_F2_PWM2}, + {"LPSS1_F3_HSUART1", BIT_LPSS1_F3_HSUART1}, + {"LPSS1_F4_HSUART2", BIT_LPSS1_F4_HSUART2}, + {"LPSS1_F5_SPI", BIT_LPSS1_F5_SPI}, + {"LPSS1_F6_Reserved", BIT_LPSS1_F6_XXX}, + {"LPSS1_F7_Reserved", BIT_LPSS1_F7_XXX}, + {"SCC_EMMC", BIT_SCC_EMMC}, + {"SCC_SDIO", BIT_SCC_SDIO}, + {"SCC_SDCARD", BIT_SCC_SDCARD}, + {"SCC_MIPI", BIT_SCC_MIPI}, + {"HDA", BIT_HDA}, + {"LPE", BIT_LPE}, + {"OTG", BIT_OTG}, + {"USH", BIT_USH}, + {"GBE", BIT_GBE}, + {"SATA", BIT_SATA}, + {"USB_EHCI", BIT_USB_EHCI}, + {"SEC", BIT_SEC}, + {"PCIE_PORT0", BIT_PCIE_PORT0}, + {"PCIE_PORT1", BIT_PCIE_PORT1}, + {"PCIE_PORT2", BIT_PCIE_PORT2}, + {"PCIE_PORT3", BIT_PCIE_PORT3}, + {"LPSS2_F0_DMA", BIT_LPSS2_F0_DMA}, + {"LPSS2_F1_I2C1", BIT_LPSS2_F1_I2C1}, + {"LPSS2_F2_I2C2", BIT_LPSS2_F2_I2C2}, + {"LPSS2_F3_I2C3", BIT_LPSS2_F3_I2C3}, + {"LPSS2_F3_I2C4", BIT_LPSS2_F4_I2C4}, + {"LPSS2_F5_I2C5", BIT_LPSS2_F5_I2C5}, + {"LPSS2_F6_I2C6", BIT_LPSS2_F6_I2C6}, + {"LPSS2_F7_I2C7", BIT_LPSS2_F7_I2C7}, + {}, +}; + +static struct pmc_bit_map byt_d3_sts_1_map[] = { + {"SMB", BIT_SMB}, + {"OTG_SS_PHY", BIT_OTG_SS_PHY}, + {"USH_SS_PHY", BIT_USH_SS_PHY}, + {"DFX", BIT_DFX}, + {}, +}; + +static struct pmc_bit_map cht_d3_sts_1_map[] = { + {"SMB", BIT_SMB}, + {"GMM", BIT_STS_GMM}, + {"ISH", BIT_STS_ISH}, + {}, +}; + +static struct pmc_bit_map cht_func_dis_2_map[] = { + {"SMB", BIT_SMB}, + {"GMM", BIT_FD_GMM}, + {"ISH", BIT_FD_ISH}, + {}, +}; + +static const struct pmc_bit_map byt_pss_map[] = { + {"GBE", PMC_PSS_BIT_GBE}, + {"SATA", PMC_PSS_BIT_SATA}, + {"HDA", PMC_PSS_BIT_HDA}, + {"SEC", PMC_PSS_BIT_SEC}, + {"PCIE", PMC_PSS_BIT_PCIE}, + {"LPSS", PMC_PSS_BIT_LPSS}, + {"LPE", PMC_PSS_BIT_LPE}, + {"DFX", PMC_PSS_BIT_DFX}, + {"USH_CTRL", PMC_PSS_BIT_USH_CTRL}, + {"USH_SUS", PMC_PSS_BIT_USH_SUS}, + {"USH_VCCS", PMC_PSS_BIT_USH_VCCS}, + {"USH_VCCA", PMC_PSS_BIT_USH_VCCA}, + {"OTG_CTRL", PMC_PSS_BIT_OTG_CTRL}, + {"OTG_VCCS", PMC_PSS_BIT_OTG_VCCS}, + {"OTG_VCCA_CLK", PMC_PSS_BIT_OTG_VCCA_CLK}, + {"OTG_VCCA", PMC_PSS_BIT_OTG_VCCA}, + {"USB", PMC_PSS_BIT_USB}, + {"USB_SUS", PMC_PSS_BIT_USB_SUS}, + {}, +}; + +static const struct pmc_bit_map cht_pss_map[] = { + {"SATA", PMC_PSS_BIT_SATA}, + {"HDA", PMC_PSS_BIT_HDA}, + {"SEC", PMC_PSS_BIT_SEC}, + {"PCIE", PMC_PSS_BIT_PCIE}, + {"LPSS", PMC_PSS_BIT_LPSS}, + {"LPE", PMC_PSS_BIT_LPE}, + {"UFS", PMC_PSS_BIT_CHT_UFS}, + {"UXD", PMC_PSS_BIT_CHT_UXD}, + {"UXD_FD", PMC_PSS_BIT_CHT_UXD_FD}, + {"UX_ENG", PMC_PSS_BIT_CHT_UX_ENG}, + {"USB_SUS", PMC_PSS_BIT_CHT_USB_SUS}, + {"GMM", PMC_PSS_BIT_CHT_GMM}, + {"ISH", PMC_PSS_BIT_CHT_ISH}, + {"DFX_MASTER", PMC_PSS_BIT_CHT_DFX_MASTER}, + {"DFX_CLUSTER1", PMC_PSS_BIT_CHT_DFX_CLUSTER1}, + {"DFX_CLUSTER2", PMC_PSS_BIT_CHT_DFX_CLUSTER2}, + {"DFX_CLUSTER3", PMC_PSS_BIT_CHT_DFX_CLUSTER3}, + {"DFX_CLUSTER4", PMC_PSS_BIT_CHT_DFX_CLUSTER4}, + {"DFX_CLUSTER5", PMC_PSS_BIT_CHT_DFX_CLUSTER5}, + {}, +}; + +static const struct pmc_reg_map byt_reg_map = { + .d3_sts_0 = d3_sts_0_map, + .d3_sts_1 = byt_d3_sts_1_map, + .func_dis = d3_sts_0_map, + .func_dis_2 = byt_d3_sts_1_map, + .pss = byt_pss_map, +}; + +static const struct pmc_reg_map cht_reg_map = { + .d3_sts_0 = d3_sts_0_map, + .d3_sts_1 = cht_d3_sts_1_map, + .func_dis = d3_sts_0_map, + .func_dis_2 = cht_func_dis_2_map, + .pss = cht_pss_map, +}; + +static inline u32 pmc_reg_read(struct pmc_dev *pmc, int reg_offset) +{ + return readl(pmc->regmap + reg_offset); +} + +static inline void pmc_reg_write(struct pmc_dev *pmc, int reg_offset, u32 val) +{ + writel(val, pmc->regmap + reg_offset); +} + +int pmc_atom_read(int offset, u32 *value) +{ + struct pmc_dev *pmc = &pmc_device; + + if (!pmc->init) + return -ENODEV; + + *value = pmc_reg_read(pmc, offset); + return 0; +} +EXPORT_SYMBOL_GPL(pmc_atom_read); + +int pmc_atom_write(int offset, u32 value) +{ + struct pmc_dev *pmc = &pmc_device; + + if (!pmc->init) + return -ENODEV; + + pmc_reg_write(pmc, offset, value); + return 0; +} +EXPORT_SYMBOL_GPL(pmc_atom_write); + +static void pmc_power_off(void) +{ + u16 pm1_cnt_port; + u32 pm1_cnt_value; + + pr_info("Preparing to enter system sleep state S5\n"); + + pm1_cnt_port = acpi_base_addr + PM1_CNT; + + pm1_cnt_value = inl(pm1_cnt_port); + pm1_cnt_value &= SLEEP_TYPE_MASK; + pm1_cnt_value |= SLEEP_TYPE_S5; + pm1_cnt_value |= SLEEP_ENABLE; + + outl(pm1_cnt_value, pm1_cnt_port); +} + +static void pmc_hw_reg_setup(struct pmc_dev *pmc) +{ + /* + * Disable PMC S0IX_WAKE_EN events coming from: + * - LPC clock run + * - GPIO_SUS ored dedicated IRQs + * - GPIO_SCORE ored dedicated IRQs + * - GPIO_SUS shared IRQ + * - GPIO_SCORE shared IRQ + */ + pmc_reg_write(pmc, PMC_S0IX_WAKE_EN, (u32)PMC_WAKE_EN_SETTING); +} + +#ifdef CONFIG_DEBUG_FS +static void pmc_dev_state_print(struct seq_file *s, int reg_index, + u32 sts, const struct pmc_bit_map *sts_map, + u32 fd, const struct pmc_bit_map *fd_map) +{ + int offset = PMC_REG_BIT_WIDTH * reg_index; + int index; + + for (index = 0; sts_map[index].name; index++) { + seq_printf(s, "Dev: %-2d - %-32s\tState: %s [%s]\n", + offset + index, sts_map[index].name, + fd_map[index].bit_mask & fd ? "Disabled" : "Enabled ", + sts_map[index].bit_mask & sts ? "D3" : "D0"); + } +} + +static int pmc_dev_state_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmc = s->private; + const struct pmc_reg_map *m = pmc->map; + u32 func_dis, func_dis_2; + u32 d3_sts_0, d3_sts_1; + + func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS); + func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2); + d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0); + d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1); + + /* Low part */ + pmc_dev_state_print(s, 0, d3_sts_0, m->d3_sts_0, func_dis, m->func_dis); + + /* High part */ + pmc_dev_state_print(s, 1, d3_sts_1, m->d3_sts_1, func_dis_2, m->func_dis_2); + + return 0; +} + +static int pmc_dev_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, pmc_dev_state_show, inode->i_private); +} + +static const struct file_operations pmc_dev_state_ops = { + .open = pmc_dev_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pmc_pss_state_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmc = s->private; + const struct pmc_bit_map *map = pmc->map->pss; + u32 pss = pmc_reg_read(pmc, PMC_PSS); + int index; + + for (index = 0; map[index].name; index++) { + seq_printf(s, "Island: %-2d - %-32s\tState: %s\n", + index, map[index].name, + map[index].bit_mask & pss ? "Off" : "On"); + } + return 0; +} + +static int pmc_pss_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, pmc_pss_state_show, inode->i_private); +} + +static const struct file_operations pmc_pss_state_ops = { + .open = pmc_pss_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pmc_sleep_tmr_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmc = s->private; + u64 s0ir_tmr, s0i1_tmr, s0i2_tmr, s0i3_tmr, s0_tmr; + + s0ir_tmr = (u64)pmc_reg_read(pmc, PMC_S0IR_TMR) << PMC_TMR_SHIFT; + s0i1_tmr = (u64)pmc_reg_read(pmc, PMC_S0I1_TMR) << PMC_TMR_SHIFT; + s0i2_tmr = (u64)pmc_reg_read(pmc, PMC_S0I2_TMR) << PMC_TMR_SHIFT; + s0i3_tmr = (u64)pmc_reg_read(pmc, PMC_S0I3_TMR) << PMC_TMR_SHIFT; + s0_tmr = (u64)pmc_reg_read(pmc, PMC_S0_TMR) << PMC_TMR_SHIFT; + + seq_printf(s, "S0IR Residency:\t%lldus\n", s0ir_tmr); + seq_printf(s, "S0I1 Residency:\t%lldus\n", s0i1_tmr); + seq_printf(s, "S0I2 Residency:\t%lldus\n", s0i2_tmr); + seq_printf(s, "S0I3 Residency:\t%lldus\n", s0i3_tmr); + seq_printf(s, "S0 Residency:\t%lldus\n", s0_tmr); + return 0; +} + +static int pmc_sleep_tmr_open(struct inode *inode, struct file *file) +{ + return single_open(file, pmc_sleep_tmr_show, inode->i_private); +} + +static const struct file_operations pmc_sleep_tmr_ops = { + .open = pmc_sleep_tmr_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void pmc_dbgfs_unregister(struct pmc_dev *pmc) +{ + debugfs_remove_recursive(pmc->dbgfs_dir); +} + +static int pmc_dbgfs_register(struct pmc_dev *pmc) +{ + struct dentry *dir, *f; + + dir = debugfs_create_dir("pmc_atom", NULL); + if (!dir) + return -ENOMEM; + + pmc->dbgfs_dir = dir; + + f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO, + dir, pmc, &pmc_dev_state_ops); + if (!f) + goto err; + + f = debugfs_create_file("pss_state", S_IFREG | S_IRUGO, + dir, pmc, &pmc_pss_state_ops); + if (!f) + goto err; + + f = debugfs_create_file("sleep_state", S_IFREG | S_IRUGO, + dir, pmc, &pmc_sleep_tmr_ops); + if (!f) + goto err; + + return 0; +err: + pmc_dbgfs_unregister(pmc); + return -ENODEV; +} +#else +static int pmc_dbgfs_register(struct pmc_dev *pmc) +{ + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct pmc_dev *pmc = &pmc_device; + const struct pmc_reg_map *map = (struct pmc_reg_map *)ent->driver_data; + int ret; + + /* Obtain ACPI base address */ + pci_read_config_dword(pdev, ACPI_BASE_ADDR_OFFSET, &acpi_base_addr); + acpi_base_addr &= ACPI_BASE_ADDR_MASK; + + /* Install power off function */ + if (acpi_base_addr != 0 && pm_power_off == NULL) + pm_power_off = pmc_power_off; + + pci_read_config_dword(pdev, PMC_BASE_ADDR_OFFSET, &pmc->base_addr); + pmc->base_addr &= PMC_BASE_ADDR_MASK; + + pmc->regmap = ioremap_nocache(pmc->base_addr, PMC_MMIO_REG_LEN); + if (!pmc->regmap) { + dev_err(&pdev->dev, "error: ioremap failed\n"); + return -ENOMEM; + } + + pmc->map = map; + + /* PMC hardware registers setup */ + pmc_hw_reg_setup(pmc); + + ret = pmc_dbgfs_register(pmc); + if (ret) + dev_warn(&pdev->dev, "debugfs register failed\n"); + + pmc->init = true; + return ret; +} + +/* + * Data for PCI driver interface + * + * used by pci_match_id() call below. + */ +static const struct pci_device_id pmc_pci_ids[] = { + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), (kernel_ulong_t)&byt_reg_map }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), (kernel_ulong_t)&cht_reg_map }, + { 0, }, +}; + +static int __init pmc_atom_init(void) +{ + struct pci_dev *pdev = NULL; + const struct pci_device_id *ent; + + /* We look for our device - PCU PMC + * we assume that there is max. one device. + * + * We can't use plain pci_driver mechanism, + * as the device is really a multiple function device, + * main driver that binds to the pci_device is lpc_ich + * and have to find & bind to the device this way. + */ + for_each_pci_dev(pdev) { + ent = pci_match_id(pmc_pci_ids, pdev); + if (ent) + return pmc_setup_dev(pdev, ent); + } + /* Device not found. */ + return -ENODEV; +} + +device_initcall(pmc_atom_init); + +/* +MODULE_AUTHOR("Aubrey Li "); +MODULE_DESCRIPTION("Intel Atom SOC Power Management Controller Interface"); +MODULE_LICENSE("GPL v2"); +*/ diff --git a/include/linux/platform_data/x86/pmc_atom.h b/include/linux/platform_data/x86/pmc_atom.h new file mode 100644 index 000000000000..aa8744c77c6d --- /dev/null +++ b/include/linux/platform_data/x86/pmc_atom.h @@ -0,0 +1,158 @@ +/* + * Intel Atom SOC Power Management Controller Header File + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + */ + +#ifndef PMC_ATOM_H +#define PMC_ATOM_H + +/* ValleyView Power Control Unit PCI Device ID */ +#define PCI_DEVICE_ID_VLV_PMC 0x0F1C +/* CherryTrail Power Control Unit PCI Device ID */ +#define PCI_DEVICE_ID_CHT_PMC 0x229C + +/* PMC Memory mapped IO registers */ +#define PMC_BASE_ADDR_OFFSET 0x44 +#define PMC_BASE_ADDR_MASK 0xFFFFFE00 +#define PMC_MMIO_REG_LEN 0x100 +#define PMC_REG_BIT_WIDTH 32 + +/* BIOS uses FUNC_DIS to disable specific function */ +#define PMC_FUNC_DIS 0x34 +#define PMC_FUNC_DIS_2 0x38 + +/* CHT specific bits in FUNC_DIS2 register */ +#define BIT_FD_GMM BIT(3) +#define BIT_FD_ISH BIT(4) + +/* S0ix wake event control */ +#define PMC_S0IX_WAKE_EN 0x3C + +#define BIT_LPC_CLOCK_RUN BIT(4) +#define BIT_SHARED_IRQ_GPSC BIT(5) +#define BIT_ORED_DEDICATED_IRQ_GPSS BIT(18) +#define BIT_ORED_DEDICATED_IRQ_GPSC BIT(19) +#define BIT_SHARED_IRQ_GPSS BIT(20) + +#define PMC_WAKE_EN_SETTING ~(BIT_LPC_CLOCK_RUN | \ + BIT_SHARED_IRQ_GPSC | \ + BIT_ORED_DEDICATED_IRQ_GPSS | \ + BIT_ORED_DEDICATED_IRQ_GPSC | \ + BIT_SHARED_IRQ_GPSS) + +/* The timers acumulate time spent in sleep state */ +#define PMC_S0IR_TMR 0x80 +#define PMC_S0I1_TMR 0x84 +#define PMC_S0I2_TMR 0x88 +#define PMC_S0I3_TMR 0x8C +#define PMC_S0_TMR 0x90 +/* Sleep state counter is in units of of 32us */ +#define PMC_TMR_SHIFT 5 + +/* Power status of power islands */ +#define PMC_PSS 0x98 + +#define PMC_PSS_BIT_GBE BIT(0) +#define PMC_PSS_BIT_SATA BIT(1) +#define PMC_PSS_BIT_HDA BIT(2) +#define PMC_PSS_BIT_SEC BIT(3) +#define PMC_PSS_BIT_PCIE BIT(4) +#define PMC_PSS_BIT_LPSS BIT(5) +#define PMC_PSS_BIT_LPE BIT(6) +#define PMC_PSS_BIT_DFX BIT(7) +#define PMC_PSS_BIT_USH_CTRL BIT(8) +#define PMC_PSS_BIT_USH_SUS BIT(9) +#define PMC_PSS_BIT_USH_VCCS BIT(10) +#define PMC_PSS_BIT_USH_VCCA BIT(11) +#define PMC_PSS_BIT_OTG_CTRL BIT(12) +#define PMC_PSS_BIT_OTG_VCCS BIT(13) +#define PMC_PSS_BIT_OTG_VCCA_CLK BIT(14) +#define PMC_PSS_BIT_OTG_VCCA BIT(15) +#define PMC_PSS_BIT_USB BIT(16) +#define PMC_PSS_BIT_USB_SUS BIT(17) + +/* CHT specific bits in PSS register */ +#define PMC_PSS_BIT_CHT_UFS BIT(7) +#define PMC_PSS_BIT_CHT_UXD BIT(11) +#define PMC_PSS_BIT_CHT_UXD_FD BIT(12) +#define PMC_PSS_BIT_CHT_UX_ENG BIT(15) +#define PMC_PSS_BIT_CHT_USB_SUS BIT(16) +#define PMC_PSS_BIT_CHT_GMM BIT(17) +#define PMC_PSS_BIT_CHT_ISH BIT(18) +#define PMC_PSS_BIT_CHT_DFX_MASTER BIT(26) +#define PMC_PSS_BIT_CHT_DFX_CLUSTER1 BIT(27) +#define PMC_PSS_BIT_CHT_DFX_CLUSTER2 BIT(28) +#define PMC_PSS_BIT_CHT_DFX_CLUSTER3 BIT(29) +#define PMC_PSS_BIT_CHT_DFX_CLUSTER4 BIT(30) +#define PMC_PSS_BIT_CHT_DFX_CLUSTER5 BIT(31) + +/* These registers reflect D3 status of functions */ +#define PMC_D3_STS_0 0xA0 + +#define BIT_LPSS1_F0_DMA BIT(0) +#define BIT_LPSS1_F1_PWM1 BIT(1) +#define BIT_LPSS1_F2_PWM2 BIT(2) +#define BIT_LPSS1_F3_HSUART1 BIT(3) +#define BIT_LPSS1_F4_HSUART2 BIT(4) +#define BIT_LPSS1_F5_SPI BIT(5) +#define BIT_LPSS1_F6_XXX BIT(6) +#define BIT_LPSS1_F7_XXX BIT(7) +#define BIT_SCC_EMMC BIT(8) +#define BIT_SCC_SDIO BIT(9) +#define BIT_SCC_SDCARD BIT(10) +#define BIT_SCC_MIPI BIT(11) +#define BIT_HDA BIT(12) +#define BIT_LPE BIT(13) +#define BIT_OTG BIT(14) +#define BIT_USH BIT(15) +#define BIT_GBE BIT(16) +#define BIT_SATA BIT(17) +#define BIT_USB_EHCI BIT(18) +#define BIT_SEC BIT(19) +#define BIT_PCIE_PORT0 BIT(20) +#define BIT_PCIE_PORT1 BIT(21) +#define BIT_PCIE_PORT2 BIT(22) +#define BIT_PCIE_PORT3 BIT(23) +#define BIT_LPSS2_F0_DMA BIT(24) +#define BIT_LPSS2_F1_I2C1 BIT(25) +#define BIT_LPSS2_F2_I2C2 BIT(26) +#define BIT_LPSS2_F3_I2C3 BIT(27) +#define BIT_LPSS2_F4_I2C4 BIT(28) +#define BIT_LPSS2_F5_I2C5 BIT(29) +#define BIT_LPSS2_F6_I2C6 BIT(30) +#define BIT_LPSS2_F7_I2C7 BIT(31) + +#define PMC_D3_STS_1 0xA4 +#define BIT_SMB BIT(0) +#define BIT_OTG_SS_PHY BIT(1) +#define BIT_USH_SS_PHY BIT(2) +#define BIT_DFX BIT(3) + +/* CHT specific bits in PMC_D3_STS_1 register */ +#define BIT_STS_GMM BIT(1) +#define BIT_STS_ISH BIT(2) + +/* PMC I/O Registers */ +#define ACPI_BASE_ADDR_OFFSET 0x40 +#define ACPI_BASE_ADDR_MASK 0xFFFFFE00 +#define ACPI_MMIO_REG_LEN 0x100 + +#define PM1_CNT 0x4 +#define SLEEP_TYPE_MASK 0xFFFFECFF +#define SLEEP_TYPE_S5 0x1C00 +#define SLEEP_ENABLE 0x2000 + +extern int pmc_atom_read(int offset, u32 *value); +extern int pmc_atom_write(int offset, u32 value); + +#endif /* PMC_ATOM_H */ -- cgit v1.2.3 From 2e6f38cef8838a5edd6051c3110ecf06f37ec544 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 23 Jan 2017 12:07:45 -0600 Subject: platform/x86: fix typo in comment s/Acumulate/Accumulate/ Signed-off-by: Pierre-Louis Bossart Acked-by: Andy Shevchenko Signed-off-by: Stephen Boyd --- include/linux/platform_data/x86/pmc_atom.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/x86/pmc_atom.h b/include/linux/platform_data/x86/pmc_atom.h index aa8744c77c6d..e4905fe69c38 100644 --- a/include/linux/platform_data/x86/pmc_atom.h +++ b/include/linux/platform_data/x86/pmc_atom.h @@ -50,7 +50,7 @@ BIT_ORED_DEDICATED_IRQ_GPSC | \ BIT_SHARED_IRQ_GPSS) -/* The timers acumulate time spent in sleep state */ +/* The timers accumulate time spent in sleep state */ #define PMC_S0IR_TMR 0x80 #define PMC_S0I1_TMR 0x84 #define PMC_S0I2_TMR 0x88 -- cgit v1.2.3 From a92def1becf33e91fc460c7ae575aa9210ba8f40 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 19 Dec 2016 18:48:29 -0200 Subject: [media] ir-rx51: port to rc-core This driver was written using lirc since rc-core did not support transmitter-only hardware at that time. Now that it does, port this driver. Compile tested only. Signed-off-by: Sean Young Cc: Timo Kokkonen Cc: Ivaylo Dimitrov Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-omap2/pdata-quirks.c | 8 +- drivers/media/rc/Kconfig | 2 +- drivers/media/rc/ir-rx51.c | 332 ++++++++++------------------ include/linux/platform_data/media/ir-rx51.h | 6 +- 4 files changed, 126 insertions(+), 222 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index 477910a48448..9f060748df5d 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -484,15 +484,15 @@ static struct pwm_omap_dmtimer_pdata pwm_dmtimer_pdata = { }; #endif -static struct lirc_rx51_platform_data __maybe_unused rx51_lirc_data = { +static struct ir_rx51_platform_data __maybe_unused rx51_ir_data = { .set_max_mpu_wakeup_lat = omap_pm_set_max_mpu_wakeup_lat, }; -static struct platform_device __maybe_unused rx51_lirc_device = { - .name = "lirc_rx51", +static struct platform_device __maybe_unused rx51_ir_device = { + .name = "ir_rx51", .id = -1, .dev = { - .platform_data = &rx51_lirc_data, + .platform_data = &rx51_ir_data, }, }; diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 3351e25d6176..d0ddbd3e22ca 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -345,7 +345,7 @@ config IR_TTUSBIR config IR_RX51 tristate "Nokia N900 IR transmitter diode" - depends on OMAP_DM_TIMER && PWM_OMAP_DMTIMER && ARCH_OMAP2PLUS && LIRC + depends on (OMAP_DM_TIMER && PWM_OMAP_DMTIMER && ARCH_OMAP2PLUS || COMPILE_TEST) && RC_CORE ---help--- Say Y or M here if you want to enable support for the IR transmitter diode built in the Nokia N900 (RX51) device. diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c index e6efa8c267a0..49265f02e772 100644 --- a/drivers/media/rc/ir-rx51.c +++ b/drivers/media/rc/ir-rx51.c @@ -15,32 +15,23 @@ */ #include #include -#include -#include #include -#include #include #include #include #include -#include -#include +#include #include -#define LIRC_RX51_DRIVER_FEATURES (LIRC_CAN_SET_SEND_DUTY_CYCLE | \ - LIRC_CAN_SET_SEND_CARRIER | \ - LIRC_CAN_SEND_PULSE) - -#define DRIVER_NAME "lirc_rx51" - #define WBUF_LEN 256 -struct lirc_rx51 { +struct ir_rx51 { + struct rc_dev *rcdev; struct pwm_device *pwm; struct hrtimer timer; struct device *dev; - struct lirc_rx51_platform_data *pdata; + struct ir_rx51_platform_data *pdata; wait_queue_head_t wqueue; unsigned int freq; /* carrier frequency */ @@ -50,38 +41,37 @@ struct lirc_rx51 { unsigned long device_is_open; }; -static inline void lirc_rx51_on(struct lirc_rx51 *lirc_rx51) +static inline void ir_rx51_on(struct ir_rx51 *ir_rx51) { - pwm_enable(lirc_rx51->pwm); + pwm_enable(ir_rx51->pwm); } -static inline void lirc_rx51_off(struct lirc_rx51 *lirc_rx51) +static inline void ir_rx51_off(struct ir_rx51 *ir_rx51) { - pwm_disable(lirc_rx51->pwm); + pwm_disable(ir_rx51->pwm); } -static int init_timing_params(struct lirc_rx51 *lirc_rx51) +static int init_timing_params(struct ir_rx51 *ir_rx51) { - struct pwm_device *pwm = lirc_rx51->pwm; - int duty, period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, lirc_rx51->freq); + struct pwm_device *pwm = ir_rx51->pwm; + int duty, period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, ir_rx51->freq); - duty = DIV_ROUND_CLOSEST(lirc_rx51->duty_cycle * period, 100); + duty = DIV_ROUND_CLOSEST(ir_rx51->duty_cycle * period, 100); pwm_config(pwm, duty, period); return 0; } -static enum hrtimer_restart lirc_rx51_timer_cb(struct hrtimer *timer) +static enum hrtimer_restart ir_rx51_timer_cb(struct hrtimer *timer) { - struct lirc_rx51 *lirc_rx51 = - container_of(timer, struct lirc_rx51, timer); + struct ir_rx51 *ir_rx51 = container_of(timer, struct ir_rx51, timer); ktime_t now; - if (lirc_rx51->wbuf_index < 0) { - dev_err_ratelimited(lirc_rx51->dev, - "BUG wbuf_index has value of %i\n", - lirc_rx51->wbuf_index); + if (ir_rx51->wbuf_index < 0) { + dev_err_ratelimited(ir_rx51->dev, + "BUG wbuf_index has value of %i\n", + ir_rx51->wbuf_index); goto end; } @@ -92,20 +82,20 @@ static enum hrtimer_restart lirc_rx51_timer_cb(struct hrtimer *timer) do { u64 ns; - if (lirc_rx51->wbuf_index >= WBUF_LEN) + if (ir_rx51->wbuf_index >= WBUF_LEN) goto end; - if (lirc_rx51->wbuf[lirc_rx51->wbuf_index] == -1) + if (ir_rx51->wbuf[ir_rx51->wbuf_index] == -1) goto end; - if (lirc_rx51->wbuf_index % 2) - lirc_rx51_off(lirc_rx51); + if (ir_rx51->wbuf_index % 2) + ir_rx51_off(ir_rx51); else - lirc_rx51_on(lirc_rx51); + ir_rx51_on(ir_rx51); - ns = 1000 * lirc_rx51->wbuf[lirc_rx51->wbuf_index]; + ns = US_TO_NS(ir_rx51->wbuf[ir_rx51->wbuf_index]); hrtimer_add_expires_ns(timer, ns); - lirc_rx51->wbuf_index++; + ir_rx51->wbuf_index++; now = timer->base->get_time(); @@ -114,203 +104,112 @@ static enum hrtimer_restart lirc_rx51_timer_cb(struct hrtimer *timer) return HRTIMER_RESTART; end: /* Stop TX here */ - lirc_rx51_off(lirc_rx51); - lirc_rx51->wbuf_index = -1; + ir_rx51_off(ir_rx51); + ir_rx51->wbuf_index = -1; - wake_up_interruptible(&lirc_rx51->wqueue); + wake_up_interruptible(&ir_rx51->wqueue); return HRTIMER_NORESTART; } -static ssize_t lirc_rx51_write(struct file *file, const char *buf, - size_t n, loff_t *ppos) +static int ir_rx51_tx(struct rc_dev *dev, unsigned int *buffer, + unsigned int count) { - int count, i; - struct lirc_rx51 *lirc_rx51 = file->private_data; + struct ir_rx51 *ir_rx51 = dev->priv; - if (n % sizeof(int)) + if (count > WBUF_LEN) return -EINVAL; - count = n / sizeof(int); - if ((count > WBUF_LEN) || (count % 2 == 0)) - return -EINVAL; + memcpy(ir_rx51->wbuf, buffer, count * sizeof(unsigned int)); /* Wait any pending transfers to finish */ - wait_event_interruptible(lirc_rx51->wqueue, lirc_rx51->wbuf_index < 0); - - if (copy_from_user(lirc_rx51->wbuf, buf, n)) - return -EFAULT; - - /* Sanity check the input pulses */ - for (i = 0; i < count; i++) - if (lirc_rx51->wbuf[i] < 0) - return -EINVAL; + wait_event_interruptible(ir_rx51->wqueue, ir_rx51->wbuf_index < 0); - init_timing_params(lirc_rx51); + init_timing_params(ir_rx51); if (count < WBUF_LEN) - lirc_rx51->wbuf[count] = -1; /* Insert termination mark */ + ir_rx51->wbuf[count] = -1; /* Insert termination mark */ /* * Adjust latency requirements so the device doesn't go in too * deep sleep states */ - lirc_rx51->pdata->set_max_mpu_wakeup_lat(lirc_rx51->dev, 50); + ir_rx51->pdata->set_max_mpu_wakeup_lat(ir_rx51->dev, 50); - lirc_rx51_on(lirc_rx51); - lirc_rx51->wbuf_index = 1; - hrtimer_start(&lirc_rx51->timer, - ns_to_ktime(1000 * lirc_rx51->wbuf[0]), + ir_rx51_on(ir_rx51); + ir_rx51->wbuf_index = 1; + hrtimer_start(&ir_rx51->timer, + ns_to_ktime(US_TO_NS(ir_rx51->wbuf[0])), HRTIMER_MODE_REL); /* * Don't return back to the userspace until the transfer has * finished */ - wait_event_interruptible(lirc_rx51->wqueue, lirc_rx51->wbuf_index < 0); + wait_event_interruptible(ir_rx51->wqueue, ir_rx51->wbuf_index < 0); /* We can sleep again */ - lirc_rx51->pdata->set_max_mpu_wakeup_lat(lirc_rx51->dev, -1); + ir_rx51->pdata->set_max_mpu_wakeup_lat(ir_rx51->dev, -1); - return n; + return count; } -static long lirc_rx51_ioctl(struct file *filep, - unsigned int cmd, unsigned long arg) +static int ir_rx51_open(struct rc_dev *dev) { - int result; - unsigned long value; - unsigned int ivalue; - struct lirc_rx51 *lirc_rx51 = filep->private_data; - - switch (cmd) { - case LIRC_GET_SEND_MODE: - result = put_user(LIRC_MODE_PULSE, (unsigned long *)arg); - if (result) - return result; - break; - - case LIRC_SET_SEND_MODE: - result = get_user(value, (unsigned long *)arg); - if (result) - return result; - - /* only LIRC_MODE_PULSE supported */ - if (value != LIRC_MODE_PULSE) - return -ENOSYS; - break; - - case LIRC_GET_REC_MODE: - result = put_user(0, (unsigned long *) arg); - if (result) - return result; - break; - - case LIRC_GET_LENGTH: - return -ENOSYS; - break; - - case LIRC_SET_SEND_DUTY_CYCLE: - result = get_user(ivalue, (unsigned int *) arg); - if (result) - return result; - - if (ivalue <= 0 || ivalue > 100) { - dev_err(lirc_rx51->dev, ": invalid duty cycle %d\n", - ivalue); - return -EINVAL; - } - - lirc_rx51->duty_cycle = ivalue; - break; - - case LIRC_SET_SEND_CARRIER: - result = get_user(ivalue, (unsigned int *) arg); - if (result) - return result; - - if (ivalue > 500000 || ivalue < 20000) { - dev_err(lirc_rx51->dev, ": invalid carrier freq %d\n", - ivalue); - return -EINVAL; - } - - lirc_rx51->freq = ivalue; - break; - - case LIRC_GET_FEATURES: - result = put_user(LIRC_RX51_DRIVER_FEATURES, - (unsigned long *) arg); - if (result) - return result; - break; - - default: - return -ENOIOCTLCMD; - } - - return 0; -} + struct ir_rx51 *ir_rx51 = dev->priv; -static int lirc_rx51_open(struct inode *inode, struct file *file) -{ - struct lirc_rx51 *lirc_rx51 = lirc_get_pdata(file); - BUG_ON(!lirc_rx51); - - file->private_data = lirc_rx51; - - if (test_and_set_bit(1, &lirc_rx51->device_is_open)) + if (test_and_set_bit(1, &ir_rx51->device_is_open)) return -EBUSY; - lirc_rx51->pwm = pwm_get(lirc_rx51->dev, NULL); - if (IS_ERR(lirc_rx51->pwm)) { - int res = PTR_ERR(lirc_rx51->pwm); + ir_rx51->pwm = pwm_get(ir_rx51->dev, NULL); + if (IS_ERR(ir_rx51->pwm)) { + int res = PTR_ERR(ir_rx51->pwm); - dev_err(lirc_rx51->dev, "pwm_get failed: %d\n", res); + dev_err(ir_rx51->dev, "pwm_get failed: %d\n", res); return res; } return 0; } -static int lirc_rx51_release(struct inode *inode, struct file *file) +static void ir_rx51_release(struct rc_dev *dev) { - struct lirc_rx51 *lirc_rx51 = file->private_data; - - hrtimer_cancel(&lirc_rx51->timer); - lirc_rx51_off(lirc_rx51); - pwm_put(lirc_rx51->pwm); + struct ir_rx51 *ir_rx51 = dev->priv; - clear_bit(1, &lirc_rx51->device_is_open); + hrtimer_cancel(&ir_rx51->timer); + ir_rx51_off(ir_rx51); + pwm_put(ir_rx51->pwm); - return 0; + clear_bit(1, &ir_rx51->device_is_open); } -static struct lirc_rx51 lirc_rx51 = { +static struct ir_rx51 ir_rx51 = { .duty_cycle = 50, .wbuf_index = -1, }; -static const struct file_operations lirc_fops = { - .owner = THIS_MODULE, - .write = lirc_rx51_write, - .unlocked_ioctl = lirc_rx51_ioctl, - .read = lirc_dev_fop_read, - .poll = lirc_dev_fop_poll, - .open = lirc_rx51_open, - .release = lirc_rx51_release, -}; +static int ir_rx51_set_duty_cycle(struct rc_dev *dev, u32 duty) +{ + struct ir_rx51 *ir_rx51 = dev->priv; -static struct lirc_driver lirc_rx51_driver = { - .name = DRIVER_NAME, - .minor = -1, - .code_length = 1, - .data = &lirc_rx51, - .fops = &lirc_fops, - .owner = THIS_MODULE, -}; + ir_rx51->duty_cycle = duty; + + return 0; +} + +static int ir_rx51_set_tx_carrier(struct rc_dev *dev, u32 carrier) +{ + struct ir_rx51 *ir_rx51 = dev->priv; + + if (carrier > 500000 || carrier < 20000) + return -EINVAL; + + ir_rx51->freq = carrier; + + return 0; +} #ifdef CONFIG_PM -static int lirc_rx51_suspend(struct platform_device *dev, pm_message_t state) +static int ir_rx51_suspend(struct platform_device *dev, pm_message_t state) { /* * In case the device is still open, do not suspend. Normally @@ -320,34 +219,34 @@ static int lirc_rx51_suspend(struct platform_device *dev, pm_message_t state) * were in a middle of a transmit. Thus, we defer any suspend * actions until transmit has completed. */ - if (test_and_set_bit(1, &lirc_rx51.device_is_open)) + if (test_and_set_bit(1, &ir_rx51.device_is_open)) return -EAGAIN; - clear_bit(1, &lirc_rx51.device_is_open); + clear_bit(1, &ir_rx51.device_is_open); return 0; } -static int lirc_rx51_resume(struct platform_device *dev) +static int ir_rx51_resume(struct platform_device *dev) { return 0; } #else -#define lirc_rx51_suspend NULL -#define lirc_rx51_resume NULL +#define ir_rx51_suspend NULL +#define ir_rx51_resume NULL #endif /* CONFIG_PM */ -static int lirc_rx51_probe(struct platform_device *dev) +static int ir_rx51_probe(struct platform_device *dev) { struct pwm_device *pwm; + struct rc_dev *rcdev; - lirc_rx51_driver.features = LIRC_RX51_DRIVER_FEATURES; - lirc_rx51.pdata = dev->dev.platform_data; + ir_rx51.pdata = dev->dev.platform_data; - if (!lirc_rx51.pdata) { + if (!ir_rx51.pdata) { dev_err(&dev->dev, "Platform Data is missing\n"); return -ENXIO; } @@ -362,51 +261,56 @@ static int lirc_rx51_probe(struct platform_device *dev) } /* Use default, in case userspace does not set the carrier */ - lirc_rx51.freq = DIV_ROUND_CLOSEST(pwm_get_period(pwm), NSEC_PER_SEC); + ir_rx51.freq = DIV_ROUND_CLOSEST(pwm_get_period(pwm), NSEC_PER_SEC); pwm_put(pwm); - hrtimer_init(&lirc_rx51.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - lirc_rx51.timer.function = lirc_rx51_timer_cb; + hrtimer_init(&ir_rx51.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ir_rx51.timer.function = ir_rx51_timer_cb; - lirc_rx51.dev = &dev->dev; - lirc_rx51_driver.dev = &dev->dev; - lirc_rx51_driver.minor = lirc_register_driver(&lirc_rx51_driver); - init_waitqueue_head(&lirc_rx51.wqueue); + ir_rx51.dev = &dev->dev; - if (lirc_rx51_driver.minor < 0) { - dev_err(lirc_rx51.dev, ": lirc_register_driver failed: %d\n", - lirc_rx51_driver.minor); - return lirc_rx51_driver.minor; - } + rcdev = devm_rc_allocate_device(&dev->dev, RC_DRIVER_IR_RAW_TX); + if (!rcdev) + return -ENOMEM; - return 0; + rcdev->priv = &ir_rx51; + rcdev->open = ir_rx51_open; + rcdev->close = ir_rx51_release; + rcdev->tx_ir = ir_rx51_tx; + rcdev->s_tx_duty_cycle = ir_rx51_set_duty_cycle; + rcdev->s_tx_carrier = ir_rx51_set_tx_carrier; + rcdev->driver_name = KBUILD_MODNAME; + + ir_rx51.rcdev = rcdev; + + return devm_rc_register_device(&dev->dev, ir_rx51.rcdev); } -static int lirc_rx51_remove(struct platform_device *dev) +static int ir_rx51_remove(struct platform_device *dev) { - return lirc_unregister_driver(lirc_rx51_driver.minor); + return 0; } -static const struct of_device_id lirc_rx51_match[] = { +static const struct of_device_id ir_rx51_match[] = { { .compatible = "nokia,n900-ir", }, {}, }; -MODULE_DEVICE_TABLE(of, lirc_rx51_match); +MODULE_DEVICE_TABLE(of, ir_rx51_match); -struct platform_driver lirc_rx51_platform_driver = { - .probe = lirc_rx51_probe, - .remove = lirc_rx51_remove, - .suspend = lirc_rx51_suspend, - .resume = lirc_rx51_resume, +static struct platform_driver ir_rx51_platform_driver = { + .probe = ir_rx51_probe, + .remove = ir_rx51_remove, + .suspend = ir_rx51_suspend, + .resume = ir_rx51_resume, .driver = { - .name = DRIVER_NAME, - .of_match_table = of_match_ptr(lirc_rx51_match), + .name = KBUILD_MODNAME, + .of_match_table = of_match_ptr(ir_rx51_match), }, }; -module_platform_driver(lirc_rx51_platform_driver); +module_platform_driver(ir_rx51_platform_driver); -MODULE_DESCRIPTION("LIRC TX driver for Nokia RX51"); +MODULE_DESCRIPTION("IR TX driver for Nokia RX51"); MODULE_AUTHOR("Nokia Corporation"); MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/media/ir-rx51.h b/include/linux/platform_data/media/ir-rx51.h index 812d87307877..2c94ab568bfa 100644 --- a/include/linux/platform_data/media/ir-rx51.h +++ b/include/linux/platform_data/media/ir-rx51.h @@ -1,7 +1,7 @@ -#ifndef _LIRC_RX51_H -#define _LIRC_RX51_H +#ifndef _IR_RX51_H +#define _IR_RX51_H -struct lirc_rx51_platform_data { +struct ir_rx51_platform_data { int(*set_max_mpu_wakeup_lat)(struct device *dev, long t); }; -- cgit v1.2.3 From 8336bf68b57e1e736be8d4ef1f46a789fe7b9bde Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 11 Jan 2017 12:28:15 +0100 Subject: mmc: mxcmmc: Include interrupt.h in the platform data header The mxcmmc platform data header depends on interrupt.h. Don't rely on the public mmc header host.h to include it, bud instead make that dependency explicit. Signed-off-by: Ulf Hansson --- include/linux/platform_data/mmc-mxcmmc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/mmc-mxcmmc.h b/include/linux/platform_data/mmc-mxcmmc.h index 29115f405af9..b0fdaa9bd185 100644 --- a/include/linux/platform_data/mmc-mxcmmc.h +++ b/include/linux/platform_data/mmc-mxcmmc.h @@ -1,6 +1,7 @@ #ifndef ASMARM_ARCH_MMC_H #define ASMARM_ARCH_MMC_H +#include #include struct device; -- cgit v1.2.3 From 55f0cd3fb9c29c20fb94c47e28a9ec8cf704f8c2 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Thu, 16 Feb 2017 13:07:37 -0700 Subject: spi: spi-ep93xx: simplify GPIO chip selects This driver requires a GPIO line to be used for the chip select of each SPI device. Remove the ep93xx_spi_chip_ops definition from the platform data and use the spi core GPIO handling for the chip selects. Fix all the ep93xx platforms that use this driver and remove the old Documentation. Signed-off-by: H Hartley Sweeten Signed-off-by: Mark Brown --- Documentation/spi/ep93xx_spi | 105 ----------------------- arch/arm/mach-ep93xx/edb93xx.c | 31 ++----- arch/arm/mach-ep93xx/simone.c | 63 +++----------- arch/arm/mach-ep93xx/vision_ep9307.c | 88 +++---------------- drivers/spi/spi-ep93xx.c | 139 ++++++++++--------------------- include/linux/platform_data/spi-ep93xx.h | 17 +--- 6 files changed, 74 insertions(+), 369 deletions(-) delete mode 100644 Documentation/spi/ep93xx_spi (limited to 'include/linux/platform_data') diff --git a/Documentation/spi/ep93xx_spi b/Documentation/spi/ep93xx_spi deleted file mode 100644 index 832ddce6e5fb..000000000000 --- a/Documentation/spi/ep93xx_spi +++ /dev/null @@ -1,105 +0,0 @@ -Cirrus EP93xx SPI controller driver HOWTO -========================================= - -ep93xx_spi driver brings SPI master support for EP93xx SPI controller. Chip -selects are implemented with GPIO lines. - -NOTE: If possible, don't use SFRMOUT (SFRM1) signal as a chip select. It will -not work correctly (it cannot be controlled by software). Use GPIO lines -instead. - -Sample configuration -==================== - -Typically driver configuration is done in platform board files (the files under -arch/arm/mach-ep93xx/*.c). In this example we configure MMC over SPI through -this driver on TS-7260 board. You can adapt the code to suit your needs. - -This example uses EGPIO9 as SD/MMC card chip select (this is wired in DIO1 -header on the board). - -You need to select CONFIG_MMC_SPI to use mmc_spi driver. - -arch/arm/mach-ep93xx/ts72xx.c: - -... -#include -#include - -#include - -/* this is our GPIO line used for chip select */ -#define MMC_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_EGPIO9 - -static int ts72xx_mmc_spi_setup(struct spi_device *spi) -{ - int err; - - err = gpio_request(MMC_CHIP_SELECT_GPIO, spi->modalias); - if (err) - return err; - - gpio_direction_output(MMC_CHIP_SELECT_GPIO, 1); - - return 0; -} - -static void ts72xx_mmc_spi_cleanup(struct spi_device *spi) -{ - gpio_set_value(MMC_CHIP_SELECT_GPIO, 1); - gpio_direction_input(MMC_CHIP_SELECT_GPIO); - gpio_free(MMC_CHIP_SELECT_GPIO); -} - -static void ts72xx_mmc_spi_cs_control(struct spi_device *spi, int value) -{ - gpio_set_value(MMC_CHIP_SELECT_GPIO, value); -} - -static struct ep93xx_spi_chip_ops ts72xx_mmc_spi_ops = { - .setup = ts72xx_mmc_spi_setup, - .cleanup = ts72xx_mmc_spi_cleanup, - .cs_control = ts72xx_mmc_spi_cs_control, -}; - -static struct spi_board_info ts72xx_spi_devices[] __initdata = { - { - .modalias = "mmc_spi", - .controller_data = &ts72xx_mmc_spi_ops, - /* - * We use 10 MHz even though the maximum is 7.4 MHz. The driver - * will limit it automatically to max. frequency. - */ - .max_speed_hz = 10 * 1000 * 1000, - .bus_num = 0, - .chip_select = 0, - .mode = SPI_MODE_0, - }, -}; - -static struct ep93xx_spi_info ts72xx_spi_info = { - .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices), -}; - -static void __init ts72xx_init_machine(void) -{ - ... - ep93xx_register_spi(&ts72xx_spi_info, ts72xx_spi_devices, - ARRAY_SIZE(ts72xx_spi_devices)); -} - -The driver can use DMA for the transfers also. In this case ts72xx_spi_info -becomes: - -static struct ep93xx_spi_info ts72xx_spi_info = { - .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices), - .use_dma = true; -}; - -Note that CONFIG_EP93XX_DMA should be enabled as well. - -Thanks to -========= -Martin Guy, H. Hartley Sweeten and others who helped me during development of -the driver. Simplemachines.it donated me a Sim.One board which I used testing -the driver on EP9307. diff --git a/arch/arm/mach-ep93xx/edb93xx.c b/arch/arm/mach-ep93xx/edb93xx.c index ad92d9f7e4df..0ac176386789 100644 --- a/arch/arm/mach-ep93xx/edb93xx.c +++ b/arch/arm/mach-ep93xx/edb93xx.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -106,33 +105,10 @@ static struct cs4271_platform_data edb93xx_cs4271_data = { .gpio_nreset = -EINVAL, /* filled in later */ }; -static int edb93xx_cs4271_hw_setup(struct spi_device *spi) -{ - return gpio_request_one(EP93XX_GPIO_LINE_EGPIO6, - GPIOF_OUT_INIT_HIGH, spi->modalias); -} - -static void edb93xx_cs4271_hw_cleanup(struct spi_device *spi) -{ - gpio_free(EP93XX_GPIO_LINE_EGPIO6); -} - -static void edb93xx_cs4271_hw_cs_control(struct spi_device *spi, int value) -{ - gpio_set_value(EP93XX_GPIO_LINE_EGPIO6, value); -} - -static struct ep93xx_spi_chip_ops edb93xx_cs4271_hw = { - .setup = edb93xx_cs4271_hw_setup, - .cleanup = edb93xx_cs4271_hw_cleanup, - .cs_control = edb93xx_cs4271_hw_cs_control, -}; - static struct spi_board_info edb93xx_spi_board_info[] __initdata = { { .modalias = "cs4271", .platform_data = &edb93xx_cs4271_data, - .controller_data = &edb93xx_cs4271_hw, .max_speed_hz = 6000000, .bus_num = 0, .chip_select = 0, @@ -140,8 +116,13 @@ static struct spi_board_info edb93xx_spi_board_info[] __initdata = { }, }; +static int edb93xx_spi_chipselects[] __initdata = { + EP93XX_GPIO_LINE_EGPIO6, +}; + static struct ep93xx_spi_info edb93xx_spi_info __initdata = { - .num_chipselect = ARRAY_SIZE(edb93xx_spi_board_info), + .chipselect = edb93xx_spi_chipselects, + .num_chipselect = ARRAY_SIZE(edb93xx_spi_chipselects), }; static void __init edb93xx_register_spi(void) diff --git a/arch/arm/mach-ep93xx/simone.c b/arch/arm/mach-ep93xx/simone.c index 7bb540c421ee..c7a40f245892 100644 --- a/arch/arm/mach-ep93xx/simone.c +++ b/arch/arm/mach-ep93xx/simone.c @@ -48,56 +48,6 @@ static struct ep93xxfb_mach_info __initdata simone_fb_info = { */ #define MMC_CARD_DETECT_GPIO EP93XX_GPIO_LINE_EGPIO0 -/* - * Up to v1.3, the Sim.One used SFRMOUT as SD card chip select, but this goes - * low between multi-message command blocks. From v1.4, it uses a GPIO instead. - * v1.3 parts will still work, since the signal on SFRMOUT is automatic. - */ -#define MMC_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_EGPIO1 - -/* - * MMC SPI chip select GPIO handling. If you are using SFRMOUT (SFRM1) signal, - * you can leave these empty and pass NULL as .controller_data. - */ - -static int simone_mmc_spi_setup(struct spi_device *spi) -{ - unsigned int gpio = MMC_CHIP_SELECT_GPIO; - int err; - - err = gpio_request(gpio, spi->modalias); - if (err) - return err; - - err = gpio_direction_output(gpio, 1); - if (err) { - gpio_free(gpio); - return err; - } - - return 0; -} - -static void simone_mmc_spi_cleanup(struct spi_device *spi) -{ - unsigned int gpio = MMC_CHIP_SELECT_GPIO; - - gpio_set_value(gpio, 1); - gpio_direction_input(gpio); - gpio_free(gpio); -} - -static void simone_mmc_spi_cs_control(struct spi_device *spi, int value) -{ - gpio_set_value(MMC_CHIP_SELECT_GPIO, value); -} - -static struct ep93xx_spi_chip_ops simone_mmc_spi_ops = { - .setup = simone_mmc_spi_setup, - .cleanup = simone_mmc_spi_cleanup, - .cs_control = simone_mmc_spi_cs_control, -}; - /* * MMC card detection GPIO setup. */ @@ -152,7 +102,6 @@ static struct mmc_spi_platform_data simone_mmc_spi_data = { static struct spi_board_info simone_spi_devices[] __initdata = { { .modalias = "mmc_spi", - .controller_data = &simone_mmc_spi_ops, .platform_data = &simone_mmc_spi_data, /* * We use 10 MHz even though the maximum is 3.7 MHz. The driver @@ -165,8 +114,18 @@ static struct spi_board_info simone_spi_devices[] __initdata = { }, }; +/* + * Up to v1.3, the Sim.One used SFRMOUT as SD card chip select, but this goes + * low between multi-message command blocks. From v1.4, it uses a GPIO instead. + * v1.3 parts will still work, since the signal on SFRMOUT is automatic. + */ +static int simone_spi_chipselects[] __initdata = { + EP93XX_GPIO_LINE_EGPIO1, +}; + static struct ep93xx_spi_info simone_spi_info __initdata = { - .num_chipselect = ARRAY_SIZE(simone_spi_devices), + .chipselect = simone_spi_chipselects, + .num_chipselect = ARRAY_SIZE(simone_spi_chipselects), .use_dma = 1, }; diff --git a/arch/arm/mach-ep93xx/vision_ep9307.c b/arch/arm/mach-ep93xx/vision_ep9307.c index 5cced5988498..1daf9441058c 100644 --- a/arch/arm/mach-ep93xx/vision_ep9307.c +++ b/arch/arm/mach-ep93xx/vision_ep9307.c @@ -175,33 +175,9 @@ static struct cs4271_platform_data vision_cs4271_data = { .gpio_nreset = EP93XX_GPIO_LINE_H(2), }; -static int vision_cs4271_hw_setup(struct spi_device *spi) -{ - return gpio_request_one(EP93XX_GPIO_LINE_EGPIO6, - GPIOF_OUT_INIT_HIGH, spi->modalias); -} - -static void vision_cs4271_hw_cleanup(struct spi_device *spi) -{ - gpio_free(EP93XX_GPIO_LINE_EGPIO6); -} - -static void vision_cs4271_hw_cs_control(struct spi_device *spi, int value) -{ - gpio_set_value(EP93XX_GPIO_LINE_EGPIO6, value); -} - -static struct ep93xx_spi_chip_ops vision_cs4271_hw = { - .setup = vision_cs4271_hw_setup, - .cleanup = vision_cs4271_hw_cleanup, - .cs_control = vision_cs4271_hw_cs_control, -}; - /************************************************************************* * SPI Flash *************************************************************************/ -#define VISION_SPI_FLASH_CS EP93XX_GPIO_LINE_EGPIO7 - static struct mtd_partition vision_spi_flash_partitions[] = { { .name = "SPI bootstrap", @@ -224,68 +200,20 @@ static struct flash_platform_data vision_spi_flash_data = { .nr_parts = ARRAY_SIZE(vision_spi_flash_partitions), }; -static int vision_spi_flash_hw_setup(struct spi_device *spi) -{ - return gpio_request_one(VISION_SPI_FLASH_CS, GPIOF_INIT_HIGH, - spi->modalias); -} - -static void vision_spi_flash_hw_cleanup(struct spi_device *spi) -{ - gpio_free(VISION_SPI_FLASH_CS); -} - -static void vision_spi_flash_hw_cs_control(struct spi_device *spi, int value) -{ - gpio_set_value(VISION_SPI_FLASH_CS, value); -} - -static struct ep93xx_spi_chip_ops vision_spi_flash_hw = { - .setup = vision_spi_flash_hw_setup, - .cleanup = vision_spi_flash_hw_cleanup, - .cs_control = vision_spi_flash_hw_cs_control, -}; - /************************************************************************* * SPI SD/MMC host *************************************************************************/ -#define VISION_SPI_MMC_CS EP93XX_GPIO_LINE_G(2) -#define VISION_SPI_MMC_WP EP93XX_GPIO_LINE_F(0) -#define VISION_SPI_MMC_CD EP93XX_GPIO_LINE_EGPIO15 - static struct mmc_spi_platform_data vision_spi_mmc_data = { .detect_delay = 100, .powerup_msecs = 100, .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, .flags = MMC_SPI_USE_CD_GPIO | MMC_SPI_USE_RO_GPIO, - .cd_gpio = VISION_SPI_MMC_CD, + .cd_gpio = EP93XX_GPIO_LINE_EGPIO15, .cd_debounce = 1, - .ro_gpio = VISION_SPI_MMC_WP, + .ro_gpio = EP93XX_GPIO_LINE_F(0), .caps2 = MMC_CAP2_RO_ACTIVE_HIGH, }; -static int vision_spi_mmc_hw_setup(struct spi_device *spi) -{ - return gpio_request_one(VISION_SPI_MMC_CS, GPIOF_INIT_HIGH, - spi->modalias); -} - -static void vision_spi_mmc_hw_cleanup(struct spi_device *spi) -{ - gpio_free(VISION_SPI_MMC_CS); -} - -static void vision_spi_mmc_hw_cs_control(struct spi_device *spi, int value) -{ - gpio_set_value(VISION_SPI_MMC_CS, value); -} - -static struct ep93xx_spi_chip_ops vision_spi_mmc_hw = { - .setup = vision_spi_mmc_hw_setup, - .cleanup = vision_spi_mmc_hw_cleanup, - .cs_control = vision_spi_mmc_hw_cs_control, -}; - /************************************************************************* * SPI Bus *************************************************************************/ @@ -293,7 +221,6 @@ static struct spi_board_info vision_spi_board_info[] __initdata = { { .modalias = "cs4271", .platform_data = &vision_cs4271_data, - .controller_data = &vision_cs4271_hw, .max_speed_hz = 6000000, .bus_num = 0, .chip_select = 0, @@ -301,7 +228,6 @@ static struct spi_board_info vision_spi_board_info[] __initdata = { }, { .modalias = "sst25l", .platform_data = &vision_spi_flash_data, - .controller_data = &vision_spi_flash_hw, .max_speed_hz = 20000000, .bus_num = 0, .chip_select = 1, @@ -309,7 +235,6 @@ static struct spi_board_info vision_spi_board_info[] __initdata = { }, { .modalias = "mmc_spi", .platform_data = &vision_spi_mmc_data, - .controller_data = &vision_spi_mmc_hw, .max_speed_hz = 20000000, .bus_num = 0, .chip_select = 2, @@ -317,8 +242,15 @@ static struct spi_board_info vision_spi_board_info[] __initdata = { }, }; +static int vision_spi_chipselects[] __initdata = { + EP93XX_GPIO_LINE_EGPIO6, + EP93XX_GPIO_LINE_EGPIO7, + EP93XX_GPIO_LINE_G(2), +}; + static struct ep93xx_spi_info vision_spi_master __initdata = { - .num_chipselect = ARRAY_SIZE(vision_spi_board_info), + .chipselect = vision_spi_chipselects, + .num_chipselect = ARRAY_SIZE(vision_spi_chipselects), .use_dma = 1, }; diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index 17a6387e20b5..b5d766064b7b 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -107,16 +108,6 @@ struct ep93xx_spi { void *zeropage; }; -/** - * struct ep93xx_spi_chip - SPI device hardware settings - * @spi: back pointer to the SPI device - * @ops: private chip operations - */ -struct ep93xx_spi_chip { - const struct spi_device *spi; - struct ep93xx_spi_chip_ops *ops; -}; - /* converts bits per word to CR0.DSS value */ #define bits_per_word_to_dss(bpw) ((bpw) - 1) @@ -229,104 +220,36 @@ static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi, return -EINVAL; } -static void ep93xx_spi_cs_control(struct spi_device *spi, bool control) -{ - struct ep93xx_spi_chip *chip = spi_get_ctldata(spi); - int value = (spi->mode & SPI_CS_HIGH) ? control : !control; - - if (chip->ops && chip->ops->cs_control) - chip->ops->cs_control(spi, value); -} - -/** - * ep93xx_spi_setup() - setup an SPI device - * @spi: SPI device to setup - * - * This function sets up SPI device mode, speed etc. Can be called multiple - * times for a single device. Returns %0 in case of success, negative error in - * case of failure. When this function returns success, the device is - * deselected. - */ -static int ep93xx_spi_setup(struct spi_device *spi) +static void ep93xx_spi_cs_control(struct spi_device *spi, bool enable) { - struct ep93xx_spi *espi = spi_master_get_devdata(spi->master); - struct ep93xx_spi_chip *chip; + if (spi->mode & SPI_CS_HIGH) + enable = !enable; - chip = spi_get_ctldata(spi); - if (!chip) { - dev_dbg(&espi->pdev->dev, "initial setup for %s\n", - spi->modalias); - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->spi = spi; - chip->ops = spi->controller_data; - - if (chip->ops && chip->ops->setup) { - int ret = chip->ops->setup(spi); - - if (ret) { - kfree(chip); - return ret; - } - } - - spi_set_ctldata(spi, chip); - } - - ep93xx_spi_cs_control(spi, false); - return 0; + if (gpio_is_valid(spi->cs_gpio)) + gpio_set_value(spi->cs_gpio, !enable); } -/** - * ep93xx_spi_cleanup() - cleans up master controller specific state - * @spi: SPI device to cleanup - * - * This function releases master controller specific state for given @spi - * device. - */ -static void ep93xx_spi_cleanup(struct spi_device *spi) -{ - struct ep93xx_spi_chip *chip; - - chip = spi_get_ctldata(spi); - if (chip) { - if (chip->ops && chip->ops->cleanup) - chip->ops->cleanup(spi); - spi_set_ctldata(spi, NULL); - kfree(chip); - } -} - -/** - * ep93xx_spi_chip_setup() - configures hardware according to given @chip - * @espi: ep93xx SPI controller struct - * @chip: chip specific settings - * @speed_hz: transfer speed - * @bits_per_word: transfer bits_per_word - */ static int ep93xx_spi_chip_setup(const struct ep93xx_spi *espi, - const struct ep93xx_spi_chip *chip, - u32 speed_hz, u8 bits_per_word) + struct spi_device *spi, + struct spi_transfer *xfer) { - u8 dss = bits_per_word_to_dss(bits_per_word); + u8 dss = bits_per_word_to_dss(xfer->bits_per_word); u8 div_cpsr = 0; u8 div_scr = 0; u16 cr0; int err; - err = ep93xx_spi_calc_divisors(espi, speed_hz, &div_cpsr, &div_scr); + err = ep93xx_spi_calc_divisors(espi, xfer->speed_hz, + &div_cpsr, &div_scr); if (err) return err; cr0 = div_scr << SSPCR0_SCR_SHIFT; - cr0 |= (chip->spi->mode & (SPI_CPHA|SPI_CPOL)) << SSPCR0_MODE_SHIFT; + cr0 |= (spi->mode & (SPI_CPHA | SPI_CPOL)) << SSPCR0_MODE_SHIFT; cr0 |= dss; dev_dbg(&espi->pdev->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n", - chip->spi->mode, div_cpsr, div_scr, dss); + spi->mode, div_cpsr, div_scr, dss); dev_dbg(&espi->pdev->dev, "setup: cr0 %#x\n", cr0); ep93xx_spi_write_u8(espi, SSPCPSR, div_cpsr); @@ -603,12 +526,11 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi, struct spi_message *msg, struct spi_transfer *t) { - struct ep93xx_spi_chip *chip = spi_get_ctldata(msg->spi); int err; msg->state = t; - err = ep93xx_spi_chip_setup(espi, chip, t->speed_hz, t->bits_per_word); + err = ep93xx_spi_chip_setup(espi, msg->spi, t); if (err) { dev_err(&espi->pdev->dev, "failed to setup chip for transfer\n"); @@ -863,8 +785,13 @@ static int ep93xx_spi_probe(struct platform_device *pdev) struct resource *res; int irq; int error; + int i; info = dev_get_platdata(&pdev->dev); + if (!info) { + dev_err(&pdev->dev, "missing platform data\n"); + return -EINVAL; + } irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -882,14 +809,36 @@ static int ep93xx_spi_probe(struct platform_device *pdev) if (!master) return -ENOMEM; - master->setup = ep93xx_spi_setup; master->transfer_one_message = ep93xx_spi_transfer_one_message; - master->cleanup = ep93xx_spi_cleanup; master->bus_num = pdev->id; - master->num_chipselect = info->num_chipselect; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + master->num_chipselect = info->num_chipselect; + master->cs_gpios = devm_kzalloc(&master->dev, + sizeof(int) * master->num_chipselect, + GFP_KERNEL); + if (!master->cs_gpios) { + error = -ENOMEM; + goto fail_release_master; + } + + for (i = 0; i < master->num_chipselect; i++) { + master->cs_gpios[i] = info->chipselect[i]; + + if (!gpio_is_valid(master->cs_gpios[i])) + continue; + + error = devm_gpio_request_one(&pdev->dev, master->cs_gpios[i], + GPIOF_OUT_INIT_HIGH, + "ep93xx-spi"); + if (error) { + dev_err(&pdev->dev, "could not request cs gpio %d\n", + master->cs_gpios[i]); + goto fail_release_master; + } + } + platform_set_drvdata(pdev, master); espi = spi_master_get_devdata(master); diff --git a/include/linux/platform_data/spi-ep93xx.h b/include/linux/platform_data/spi-ep93xx.h index 9bb63ac13f04..171a271c2cbd 100644 --- a/include/linux/platform_data/spi-ep93xx.h +++ b/include/linux/platform_data/spi-ep93xx.h @@ -5,25 +5,14 @@ struct spi_device; /** * struct ep93xx_spi_info - EP93xx specific SPI descriptor - * @num_chipselect: number of chip selects on this board, must be - * at least one + * @chipselect: array of gpio numbers to use as chip selects + * @num_chipselect: ARRAY_SIZE(chipselect) * @use_dma: use DMA for the transfers */ struct ep93xx_spi_info { + int *chipselect; int num_chipselect; bool use_dma; }; -/** - * struct ep93xx_spi_chip_ops - operation callbacks for SPI slave device - * @setup: setup the chip select mechanism - * @cleanup: cleanup the chip select mechanism - * @cs_control: control the device chip select - */ -struct ep93xx_spi_chip_ops { - int (*setup)(struct spi_device *spi); - void (*cleanup)(struct spi_device *spi); - void (*cs_control)(struct spi_device *spi, int value); -}; - #endif /* __ASM_MACH_EP93XX_SPI_H */ -- cgit v1.2.3 From 0500ce589aa7b5325af161d3c992ffb6be138ff9 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 15 Feb 2017 09:35:27 -0700 Subject: rtc: m48t86: remove unused platform_data All users of this driver have been updated to allow the driver to manage it's own resources and do the read/write operations internally. The m48t86_ops are no longer used. Remove the platform_data header and the support code in the driver. Signed-off-by: H Hartley Sweeten Signed-off-by: Alexandre Belloni --- drivers/rtc/rtc-m48t86.c | 51 ++++++++++++-------------------- include/linux/platform_data/rtc-m48t86.h | 16 ---------- 2 files changed, 19 insertions(+), 48 deletions(-) delete mode 100644 include/linux/platform_data/rtc-m48t86.h (limited to 'include/linux/platform_data') diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c index 491e8e4b300b..02af045305dd 100644 --- a/drivers/rtc/rtc-m48t86.c +++ b/drivers/rtc/rtc-m48t86.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -45,7 +44,6 @@ struct m48t86_rtc_info { void __iomem *index_reg; void __iomem *data_reg; struct rtc_device *rtc; - struct m48t86_ops *ops; }; static unsigned char m48t86_readb(struct device *dev, unsigned long addr) @@ -53,12 +51,9 @@ static unsigned char m48t86_readb(struct device *dev, unsigned long addr) struct m48t86_rtc_info *info = dev_get_drvdata(dev); unsigned char value; - if (info->ops) { - value = info->ops->readbyte(addr); - } else { - writeb(addr, info->index_reg); - value = readb(info->data_reg); - } + writeb(addr, info->index_reg); + value = readb(info->data_reg); + return value; } @@ -67,12 +62,8 @@ static void m48t86_writeb(struct device *dev, { struct m48t86_rtc_info *info = dev_get_drvdata(dev); - if (info->ops) { - info->ops->writebyte(value, addr); - } else { - writeb(addr, info->index_reg); - writeb(value, info->data_reg); - } + writeb(addr, info->index_reg); + writeb(value, info->data_reg); } static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) @@ -235,30 +226,26 @@ static bool m48t86_verify_chip(struct platform_device *pdev) static int m48t86_rtc_probe(struct platform_device *pdev) { struct m48t86_rtc_info *info; + struct resource *res; unsigned char reg; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; - info->ops = dev_get_platdata(&pdev->dev); - if (!info->ops) { - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - info->index_reg = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(info->index_reg)) - return PTR_ERR(info->index_reg); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) - return -ENODEV; - info->data_reg = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(info->data_reg)) - return PTR_ERR(info->data_reg); - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + info->index_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->index_reg)) + return PTR_ERR(info->index_reg); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -ENODEV; + info->data_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->data_reg)) + return PTR_ERR(info->data_reg); dev_set_drvdata(&pdev->dev, info); diff --git a/include/linux/platform_data/rtc-m48t86.h b/include/linux/platform_data/rtc-m48t86.h deleted file mode 100644 index 915d6b4f0f89..000000000000 --- a/include/linux/platform_data/rtc-m48t86.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * ST M48T86 / Dallas DS12887 RTC driver - * Copyright (c) 2006 Tower Technologies - * - * Author: Alessandro Zummo - * - * 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. -*/ - -struct m48t86_ops -{ - void (*writebyte)(unsigned char value, unsigned long addr); - unsigned char (*readbyte)(unsigned long addr); -}; -- cgit v1.2.3