From 44773ba170a6f969620221a6d87d03feae5e464f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 16 Apr 2018 10:22:01 -0700 Subject: ARM: OMAP2+: Drop unused pm-noop Looks like these functions don't do anything in the mainline kernel so we can just drop it. Note that we must now also remove ir-rx51 pdata as it relies on the dummy platform data that does not do anything. And ir-rx51 is calling a pdata callback that doesn't do anything without checking if it exists first. For configuring device specific minimal latencies, the interface to use is pm_qos_add_request(). For an example, see what was done in commit 9834ffd1ecc3 ("ASoC: omap-mcbsp: Add PM QoS support for McBSP to prevent glitches"). I've added some comments to ir-rx51 so people using it can add pm_qos support and test it. Cc: Ivaylo Dimitrov Cc: Kevin Hilman Cc: Laurent Pinchart Cc: Tomi Valkeinen Acked-by: Mauro Carvalho Chehab Signed-off-by: Tony Lindgren --- include/linux/platform_data/media/ir-rx51.h | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 include/linux/platform_data/media/ir-rx51.h (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/media/ir-rx51.h b/include/linux/platform_data/media/ir-rx51.h deleted file mode 100644 index 9d127aa648e7..000000000000 --- a/include/linux/platform_data/media/ir-rx51.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _IR_RX51_H -#define _IR_RX51_H - -struct ir_rx51_platform_data { - int(*set_max_mpu_wakeup_lat)(struct device *dev, long t); -}; - -#endif -- cgit v1.2.3 From 8b4aa4d8dc283a408eb14ca5e27e0a9a689c2e5a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 5 Apr 2018 12:03:05 -0400 Subject: media: mmp-camera.h: add missing platform data Those definitions used to be part of the original patch: https://patchwork.kernel.org/patch/2815221/ But, somehow, nobody ever noticed until today. Years later, Arnd discovered that mmp-camera driver doesn't build and make it depend on BROKEN. Add the missing bits here, in order to remove BROKEN dependency. Signed-off-by: Mauro Carvalho Chehab --- include/linux/platform_data/media/mmp-camera.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/media/mmp-camera.h b/include/linux/platform_data/media/mmp-camera.h index 83804028115c..d2d3a443eedf 100644 --- a/include/linux/platform_data/media/mmp-camera.h +++ b/include/linux/platform_data/media/mmp-camera.h @@ -3,8 +3,27 @@ * Information for the Marvell Armada MMP camera */ +#include + +enum dphy3_algo { + DPHY3_ALGO_DEFAULT = 0, + DPHY3_ALGO_PXA910, + DPHY3_ALGO_PXA2128 +}; + struct mmp_camera_platform_data { struct platform_device *i2c_device; int sensor_power_gpio; int sensor_reset_gpio; + enum v4l2_mbus_type bus_type; + int mclk_min; /* The minimal value of MCLK */ + int mclk_src; /* which clock source the MCLK derives from */ + int mclk_div; /* Clock Divider Value for MCLK */ + /* + * MIPI support + */ + int dphy[3]; /* DPHY: CSI2_DPHY3, CSI2_DPHY5, CSI2_DPHY6 */ + enum dphy3_algo dphy3_algo; /* algos for calculate CSI2_DPHY3 */ + int lane; /* ccic used lane number; 0 means DVP mode */ + int lane_clk; }; -- cgit v1.2.3 From fe1bd78bf18a7cb3eb76fceea9193534fb6619e3 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Mon, 2 Apr 2018 13:06:05 -0700 Subject: ARM: imx: Update spi_imx platform data to reflect current state The docs for the spi_imx platform data still refer to a -32 offset used to specify a native chip select. This was removed in commit 602c8f4485cd ("spi: imx: fix use of native chip-selects with devicetree") and no longer works as documented. Update documentation. The macro MXC_SPI_CS() is no longer is needed. If a board uses all native chip selects, then it's not necessary to specify a chip select array at all, as all native is the default (this is how device-tree configured SPI masters work too). Most of the spi-imx platform data users have their chip select arrays removed by this patch. This patch also fixes a bug in mx31moboard introduced in the '602 commit. When that board was updated in commit 901f26bce64a ("ARM: imx: set correct chip_select in platform setup") to reflect the SPI change, only SPI bus 2 was updated and SPI bus 1 was left with non-sequential chip selects. The mc13783 spi device on bus 1 had its chip select updated as if it were on bus 2. CC: Sascha Hauer CC: Fabio Estevam Acked-by: Greg Ungerer Reviewed-by: Oleksij Rempel Signed-off-by: Trent Piepho Signed-off-by: Shawn Guo --- arch/arm/mach-imx/mach-mx31_3ds.c | 18 ++---------------- arch/arm/mach-imx/mach-mx31lilly.c | 12 ++---------- arch/arm/mach-imx/mach-mx31lite.c | 16 ++-------------- arch/arm/mach-imx/mach-mx31moboard.c | 17 +++-------------- arch/arm/mach-imx/mach-pcm037_eet.c | 5 +---- include/linux/platform_data/spi-imx.h | 29 +++++++++++++++++------------ 6 files changed, 27 insertions(+), 70 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-imx/mach-mx31_3ds.c b/arch/arm/mach-imx/mach-mx31_3ds.c index 68c3f0799d5b..9d87f1dcf7bb 100644 --- a/arch/arm/mach-imx/mach-mx31_3ds.c +++ b/arch/arm/mach-imx/mach-mx31_3ds.c @@ -374,26 +374,12 @@ static struct imx_ssi_platform_data mx31_3ds_ssi_pdata = { }; /* SPI */ -static int spi0_internal_chipselect[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(1), - MXC_SPI_CS(2), -}; - static const struct spi_imx_master spi0_pdata __initconst = { - .chipselect = spi0_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi0_internal_chipselect), -}; - -static int spi1_internal_chipselect[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(1), - MXC_SPI_CS(2), + .num_chipselect = 3, }; static const struct spi_imx_master spi1_pdata __initconst = { - .chipselect = spi1_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi1_internal_chipselect), + .num_chipselect = 3, }; static struct spi_board_info mx31_3ds_spi_devs[] __initdata = { diff --git a/arch/arm/mach-imx/mach-mx31lilly.c b/arch/arm/mach-imx/mach-mx31lilly.c index 6fd463642954..8bf52819d4d9 100644 --- a/arch/arm/mach-imx/mach-mx31lilly.c +++ b/arch/arm/mach-imx/mach-mx31lilly.c @@ -226,20 +226,12 @@ static void __init lilly1131_usb_init(void) /* SPI */ -static int spi_internal_chipselect[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(1), - MXC_SPI_CS(2), -}; - static const struct spi_imx_master spi0_pdata __initconst = { - .chipselect = spi_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi_internal_chipselect), + .num_chipselect = 3, }; static const struct spi_imx_master spi1_pdata __initconst = { - .chipselect = spi_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi_internal_chipselect), + .num_chipselect = 3, }; static struct mc13xxx_platform_data mc13783_pdata __initdata = { diff --git a/arch/arm/mach-imx/mach-mx31lite.c b/arch/arm/mach-imx/mach-mx31lite.c index a3250bc7f114..a3cbba6c955b 100644 --- a/arch/arm/mach-imx/mach-mx31lite.c +++ b/arch/arm/mach-imx/mach-mx31lite.c @@ -83,15 +83,8 @@ static const struct imxuart_platform_data uart_pdata __initconst = { }; /* SPI */ -static int spi0_internal_chipselect[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(1), - MXC_SPI_CS(2), -}; - static const struct spi_imx_master spi0_pdata __initconst = { - .chipselect = spi0_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi0_internal_chipselect), + .num_chipselect = 3, }; static const struct mxc_nand_platform_data @@ -133,13 +126,8 @@ static struct platform_device smsc911x_device = { * The MC13783 is the only hard-wired SPI device on the module. */ -static int spi1_internal_chipselect[] = { - MXC_SPI_CS(0), -}; - static const struct spi_imx_master spi1_pdata __initconst = { - .chipselect = spi1_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi1_internal_chipselect), + .num_chipselect = 1, }; static struct mc13xxx_platform_data mc13783_pdata __initdata = { diff --git a/arch/arm/mach-imx/mach-mx31moboard.c b/arch/arm/mach-imx/mach-mx31moboard.c index 7716f83aecdd..643a3d749703 100644 --- a/arch/arm/mach-imx/mach-mx31moboard.c +++ b/arch/arm/mach-imx/mach-mx31moboard.c @@ -152,14 +152,8 @@ static const struct imxi2c_platform_data moboard_i2c1_data __initconst = { .bitrate = 100000, }; -static int moboard_spi1_cs[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(2), -}; - static const struct spi_imx_master moboard_spi1_pdata __initconst = { - .chipselect = moboard_spi1_cs, - .num_chipselect = ARRAY_SIZE(moboard_spi1_cs), + .num_chipselect = 3, }; static struct regulator_consumer_supply sdhc_consumers[] = { @@ -296,19 +290,14 @@ static struct spi_board_info moboard_spi_board_info[] __initdata = { /* irq number is run-time assigned */ .max_speed_hz = 300000, .bus_num = 1, - .chip_select = 1, + .chip_select = 0, .platform_data = &moboard_pmic, .mode = SPI_CS_HIGH, }, }; -static int moboard_spi2_cs[] = { - MXC_SPI_CS(0), MXC_SPI_CS(1), -}; - static const struct spi_imx_master moboard_spi2_pdata __initconst = { - .chipselect = moboard_spi2_cs, - .num_chipselect = ARRAY_SIZE(moboard_spi2_cs), + .num_chipselect = 2, }; #define SDHC1_CD IOMUX_TO_GPIO(MX31_PIN_ATA_CS0) diff --git a/arch/arm/mach-imx/mach-pcm037_eet.c b/arch/arm/mach-imx/mach-pcm037_eet.c index 95bd97710494..15bc956d466b 100644 --- a/arch/arm/mach-imx/mach-pcm037_eet.c +++ b/arch/arm/mach-imx/mach-pcm037_eet.c @@ -56,11 +56,8 @@ static struct spi_board_info pcm037_spi_dev[] = { }; /* Platform Data for MXC CSPI */ -static int pcm037_spi1_cs[] = { MXC_SPI_CS(0), MXC_SPI_CS(1), }; - static const struct spi_imx_master pcm037_spi1_pdata __initconst = { - .chipselect = pcm037_spi1_cs, - .num_chipselect = ARRAY_SIZE(pcm037_spi1_cs), + .num_chipselect = 2, }; /* GPIO-keys input device */ diff --git a/include/linux/platform_data/spi-imx.h b/include/linux/platform_data/spi-imx.h index 6f012fefa1a2..328f670d10bd 100644 --- a/include/linux/platform_data/spi-imx.h +++ b/include/linux/platform_data/spi-imx.h @@ -5,24 +5,29 @@ /* * struct spi_imx_master - device.platform_data for SPI controller devices. - * @chipselect: Array of chipselects for this master. Numbers >= 0 mean gpio - * pins, numbers < 0 mean internal CSPI chipselects according - * to MXC_SPI_CS(). Normally you want to use gpio based chip - * selects as the CSPI module tries to be intelligent about - * when to assert the chipselect: The CSPI module deasserts the - * chipselect once it runs out of input data. The other problem - * is that it is not possible to mix between high active and low - * active chipselects on one single bus using the internal - * chipselects. Unfortunately Freescale decided to put some + * @chipselect: Array of chipselects for this master or NULL. Numbers >= 0 + * mean GPIO pins, -ENOENT means internal CSPI chipselect + * matching the position in the array. E.g., if chipselect[1] = + * -ENOENT then a SPI slave using chip select 1 will use the + * native SS1 line of the CSPI. Omitting the array will use + * all native chip selects. + + * Normally you want to use gpio based chip selects as the CSPI + * module tries to be intelligent about when to assert the + * chipselect: The CSPI module deasserts the chipselect once it + * runs out of input data. The other problem is that it is not + * possible to mix between high active and low active chipselects + * on one single bus using the internal chipselects. + * Unfortunately, on some SoCs, Freescale decided to put some * chipselects on dedicated pins which are not usable as gpios, * so we have to support the internal chipselects. - * @num_chipselect: ARRAY_SIZE(chipselect) + * + * @num_chipselect: If @chipselect is specified, ARRAY_SIZE(chipselect), + * otherwise the number of native chip selects. */ struct spi_imx_master { int *chipselect; int num_chipselect; }; -#define MXC_SPI_CS(no) ((no) - 32) - #endif /* __MACH_SPI_H_*/ -- cgit v1.2.3 From 9e4d60938a2b2aae3c2c213c923d9eb8d0a87ba2 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 19 Apr 2018 01:02:50 +0200 Subject: net: phy: mdio-gpio: Remove reset function The platform data can contain a function to call to reset the bit banging interface. It is not used, so remove it. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio-gpio.c | 1 - include/linux/platform_data/mdio-gpio.h | 2 -- 2 files changed, 3 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 369f5f35d6fd..570b87b82abc 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -141,7 +141,6 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, goto out; bitbang->ctrl.ops = &mdio_gpio_ops; - bitbang->ctrl.reset = pdata->reset; mdc = pdata->mdc; bitbang->mdc = gpio_to_desc(mdc); if (pdata->mdc_active_low) diff --git a/include/linux/platform_data/mdio-gpio.h b/include/linux/platform_data/mdio-gpio.h index 11f00cdabe3d..6e8f01a570f2 100644 --- a/include/linux/platform_data/mdio-gpio.h +++ b/include/linux/platform_data/mdio-gpio.h @@ -26,8 +26,6 @@ struct mdio_gpio_platform_data { u32 phy_mask; u32 phy_ignore_ta_mask; int irqs[PHY_MAX_ADDR]; - /* reset callback */ - int (*reset)(struct mii_bus *bus); }; #endif /* __LINUX_MDIO_GPIO_H */ -- cgit v1.2.3 From c1b3eb04682f80af74572aa25601dbb555c6bc6e Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 19 Apr 2018 01:02:52 +0200 Subject: net: phy: mdio-gpio: remove support for ignoring turn around This is not needed any more by devices using platform data, so remove it. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio-gpio.c | 1 - include/linux/platform_data/mdio-gpio.h | 1 - 2 files changed, 2 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 570b87b82abc..28ad9ca7b9e7 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -163,7 +163,6 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, new_bus->name = "GPIO Bitbanged MDIO"; new_bus->phy_mask = pdata->phy_mask; - new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask; memcpy(new_bus->irq, pdata->irqs, sizeof(new_bus->irq)); new_bus->parent = dev; diff --git a/include/linux/platform_data/mdio-gpio.h b/include/linux/platform_data/mdio-gpio.h index 6e8f01a570f2..b8f914a30126 100644 --- a/include/linux/platform_data/mdio-gpio.h +++ b/include/linux/platform_data/mdio-gpio.h @@ -24,7 +24,6 @@ struct mdio_gpio_platform_data { bool mdo_active_low; u32 phy_mask; - u32 phy_ignore_ta_mask; int irqs[PHY_MAX_ADDR]; }; -- cgit v1.2.3 From 185a16b60a239f9db3fe8b8ce931a2fd61330853 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 19 Apr 2018 01:02:53 +0200 Subject: net: phy: mdio-gpio: remove support for phy mask This is not needed any more by devices using platform data, so remove it. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio-gpio.c | 4 ---- include/linux/platform_data/mdio-gpio.h | 1 - 2 files changed, 5 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 28ad9ca7b9e7..676ba0dd04be 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -162,13 +162,9 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, new_bus->name = "GPIO Bitbanged MDIO"; - new_bus->phy_mask = pdata->phy_mask; memcpy(new_bus->irq, pdata->irqs, sizeof(new_bus->irq)); new_bus->parent = dev; - if (new_bus->phy_mask == ~0) - goto out_free_bus; - for (i = 0; i < PHY_MAX_ADDR; i++) if (!new_bus->irq[i]) new_bus->irq[i] = PHY_POLL; diff --git a/include/linux/platform_data/mdio-gpio.h b/include/linux/platform_data/mdio-gpio.h index b8f914a30126..7d55dfef56dc 100644 --- a/include/linux/platform_data/mdio-gpio.h +++ b/include/linux/platform_data/mdio-gpio.h @@ -23,7 +23,6 @@ struct mdio_gpio_platform_data { bool mdio_active_low; bool mdo_active_low; - u32 phy_mask; int irqs[PHY_MAX_ADDR]; }; -- cgit v1.2.3 From 68abb4f25d7eaf4d5f40108aa748621ddab68209 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 19 Apr 2018 01:02:54 +0200 Subject: net: phy: mdio-gpio: Remove support for IRQs in platform data No current devices use IRQs in platform data, so remove support for it. The MDIO core will also initialise the new bus such that all addresses are polled, so remove the unneeded re-initialisation. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio-gpio.c | 7 ------- include/linux/platform_data/mdio-gpio.h | 2 -- 2 files changed, 9 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 676ba0dd04be..0f8c748c8edd 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -130,7 +130,6 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, { struct mii_bus *new_bus; struct mdio_gpio_info *bitbang; - int i; int mdc, mdio, mdo; unsigned long mdc_flags = GPIOF_OUT_INIT_LOW; unsigned long mdio_flags = GPIOF_DIR_IN; @@ -161,14 +160,8 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, goto out; new_bus->name = "GPIO Bitbanged MDIO"; - - memcpy(new_bus->irq, pdata->irqs, sizeof(new_bus->irq)); new_bus->parent = dev; - for (i = 0; i < PHY_MAX_ADDR; i++) - if (!new_bus->irq[i]) - new_bus->irq[i] = PHY_POLL; - if (bus_id != -1) snprintf(new_bus->id, MII_BUS_ID_SIZE, "gpio-%x", bus_id); else diff --git a/include/linux/platform_data/mdio-gpio.h b/include/linux/platform_data/mdio-gpio.h index 7d55dfef56dc..af3be0c4ff9b 100644 --- a/include/linux/platform_data/mdio-gpio.h +++ b/include/linux/platform_data/mdio-gpio.h @@ -22,8 +22,6 @@ struct mdio_gpio_platform_data { bool mdc_active_low; bool mdio_active_low; bool mdo_active_low; - - int irqs[PHY_MAX_ADDR]; }; #endif /* __LINUX_MDIO_GPIO_H */ -- cgit v1.2.3 From c82fc4814a93f36c9b536b5af8dd617e4ee1380f Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 19 Apr 2018 01:02:55 +0200 Subject: net: phy: mdio-gpio: Swap to using gpio descriptors This simplifies the code, removing the need to handle active low flags, etc. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio-gpio.c | 73 ++++++++------------------------- include/linux/platform_data/mdio-gpio.h | 10 ++--- 2 files changed, 20 insertions(+), 63 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 0f8c748c8edd..b999f32374e2 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -35,35 +35,25 @@ struct mdio_gpio_info { struct gpio_desc *mdc, *mdio, *mdo; }; -static void *mdio_gpio_of_get_data(struct platform_device *pdev) +static void *mdio_gpio_of_get_data(struct device *dev) { - struct device_node *np = pdev->dev.of_node; struct mdio_gpio_platform_data *pdata; - enum of_gpio_flags flags; - int ret; - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; - ret = of_get_gpio_flags(np, 0, &flags); - if (ret < 0) - return NULL; - - pdata->mdc = ret; - pdata->mdc_active_low = flags & OF_GPIO_ACTIVE_LOW; + pdata->mdc = devm_gpiod_get_index(dev, NULL, 0, GPIOD_OUT_LOW); + if (IS_ERR(pdata->mdc)) + return ERR_CAST(pdata->mdc); - ret = of_get_gpio_flags(np, 1, &flags); - if (ret < 0) - return NULL; - pdata->mdio = ret; - pdata->mdio_active_low = flags & OF_GPIO_ACTIVE_LOW; + pdata->mdio = devm_gpiod_get_index(dev, NULL, 1, GPIOD_IN); + if (IS_ERR(pdata->mdio)) + return ERR_CAST(pdata->mdio); - ret = of_get_gpio_flags(np, 2, &flags); - if (ret > 0) { - pdata->mdo = ret; - pdata->mdo_active_low = flags & OF_GPIO_ACTIVE_LOW; - } + pdata->mdo = devm_gpiod_get_index_optional(dev, NULL, 2, GPIOD_OUT_LOW); + if (IS_ERR(pdata->mdo)) + return ERR_CAST(pdata->mdo); return pdata; } @@ -130,34 +120,19 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, { struct mii_bus *new_bus; struct mdio_gpio_info *bitbang; - int mdc, mdio, mdo; - unsigned long mdc_flags = GPIOF_OUT_INIT_LOW; - unsigned long mdio_flags = GPIOF_DIR_IN; - unsigned long mdo_flags = GPIOF_OUT_INIT_HIGH; bitbang = devm_kzalloc(dev, sizeof(*bitbang), GFP_KERNEL); if (!bitbang) - goto out; + return NULL; bitbang->ctrl.ops = &mdio_gpio_ops; - mdc = pdata->mdc; - bitbang->mdc = gpio_to_desc(mdc); - if (pdata->mdc_active_low) - mdc_flags = GPIOF_OUT_INIT_HIGH | GPIOF_ACTIVE_LOW; - mdio = pdata->mdio; - bitbang->mdio = gpio_to_desc(mdio); - if (pdata->mdio_active_low) - mdio_flags |= GPIOF_ACTIVE_LOW; - mdo = pdata->mdo; - if (mdo) { - bitbang->mdo = gpio_to_desc(mdo); - if (pdata->mdo_active_low) - mdo_flags = GPIOF_OUT_INIT_LOW | GPIOF_ACTIVE_LOW; - } + bitbang->mdc = pdata->mdc; + bitbang->mdio = pdata->mdio; + bitbang->mdo = pdata->mdo; new_bus = alloc_mdio_bitbang(&bitbang->ctrl); if (!new_bus) - goto out; + return NULL; new_bus->name = "GPIO Bitbanged MDIO"; new_bus->parent = dev; @@ -167,23 +142,9 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, else strncpy(new_bus->id, "gpio", MII_BUS_ID_SIZE); - if (devm_gpio_request_one(dev, mdc, mdc_flags, "mdc")) - goto out_free_bus; - - if (devm_gpio_request_one(dev, mdio, mdio_flags, "mdio")) - goto out_free_bus; - - if (mdo && devm_gpio_request_one(dev, mdo, mdo_flags, "mdo")) - goto out_free_bus; - dev_set_drvdata(dev, new_bus); return new_bus; - -out_free_bus: - free_mdio_bitbang(new_bus); -out: - return NULL; } static void mdio_gpio_bus_deinit(struct device *dev) @@ -208,7 +169,7 @@ static int mdio_gpio_probe(struct platform_device *pdev) int ret, bus_id; if (pdev->dev.of_node) { - pdata = mdio_gpio_of_get_data(pdev); + pdata = mdio_gpio_of_get_data(&pdev->dev); bus_id = of_alias_get_id(pdev->dev.of_node, "mdio-gpio"); if (bus_id < 0) { dev_warn(&pdev->dev, "failed to get alias id\n"); diff --git a/include/linux/platform_data/mdio-gpio.h b/include/linux/platform_data/mdio-gpio.h index af3be0c4ff9b..bd91fa98a3aa 100644 --- a/include/linux/platform_data/mdio-gpio.h +++ b/include/linux/platform_data/mdio-gpio.h @@ -15,13 +15,9 @@ struct mdio_gpio_platform_data { /* GPIO numbers for bus pins */ - unsigned int mdc; - unsigned int mdio; - unsigned int mdo; - - bool mdc_active_low; - bool mdio_active_low; - bool mdo_active_low; + struct gpio_desc *mdc; + struct gpio_desc *mdio; + struct gpio_desc *mdo; }; #endif /* __LINUX_MDIO_GPIO_H */ -- cgit v1.2.3 From 0207dd1173fe31c153ffd439c4bb33d1341829b1 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 19 Apr 2018 01:02:59 +0200 Subject: net: phy: mdio-gpio: Remove redundant platform data header The platform data header file is now unused. Remove it, but add an extra include which it brought in. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- MAINTAINERS | 1 - drivers/net/phy/mdio-gpio.c | 2 +- include/linux/platform_data/mdio-gpio.h | 23 ----------------------- 3 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 include/linux/platform_data/mdio-gpio.h (limited to 'include/linux/platform_data') diff --git a/MAINTAINERS b/MAINTAINERS index b60179d948bb..a7321687cae9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5321,7 +5321,6 @@ F: include/linux/*mdio*.h F: include/linux/of_net.h F: include/linux/phy.h F: include/linux/phy_fixed.h -F: include/linux/platform_data/mdio-gpio.h F: include/linux/platform_data/mdio-bcm-unimac.h F: include/trace/events/mdio.h F: include/uapi/linux/mdio.h diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 281c905ef9fd..b501221819e1 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include diff --git a/include/linux/platform_data/mdio-gpio.h b/include/linux/platform_data/mdio-gpio.h deleted file mode 100644 index bd91fa98a3aa..000000000000 --- a/include/linux/platform_data/mdio-gpio.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * MDIO-GPIO bus platform data structures - * - * Copyright (C) 2008, Paulius Zaleckas - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#ifndef __LINUX_MDIO_GPIO_H -#define __LINUX_MDIO_GPIO_H - -#include - -struct mdio_gpio_platform_data { - /* GPIO numbers for bus pins */ - struct gpio_desc *mdc; - struct gpio_desc *mdio; - struct gpio_desc *mdo; -}; - -#endif /* __LINUX_MDIO_GPIO_H */ -- cgit v1.2.3 From 8af70cd2ca508061088d5059ba8a8218aca7ddf1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 20 Apr 2018 10:14:27 -0700 Subject: memory: aemif: add support for board files Currently aemif is supported in two places separately. By the platform driver in drivers/memory and by a hand crafted driver in mach-davinci. We want to drop the latter but also keep the legacy mode. Add support for board files to the aemif driver. The new structure in platform data currently only contains the chip select number, since currently existing users don't require anything else, but it can be extended in the future. While extending the platform data struct, add kernel docs describing its members. Signed-off-by: Bartosz Golaszewski Signed-off-by: Santosh Shilimkar --- drivers/memory/ti-aemif.c | 58 ++++++++++++++++++++++------------ include/linux/platform_data/ti-aemif.h | 25 +++++++++++++++ 2 files changed, 63 insertions(+), 20 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c index 588e58d40d1b..31112f622b88 100644 --- a/drivers/memory/ti-aemif.c +++ b/drivers/memory/ti-aemif.c @@ -339,9 +339,6 @@ static int aemif_probe(struct platform_device *pdev) struct aemif_platform_data *pdata; struct of_dev_auxdata *dev_lookup; - if (np == NULL) - return 0; - aemif = devm_kzalloc(dev, sizeof(*aemif), GFP_KERNEL); if (!aemif) return -ENOMEM; @@ -363,8 +360,10 @@ static int aemif_probe(struct platform_device *pdev) aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC; - if (of_device_is_compatible(np, "ti,da850-aemif")) + if (np && of_device_is_compatible(np, "ti,da850-aemif")) aemif->cs_offset = 2; + else if (pdata) + aemif->cs_offset = pdata->cs_offset; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); aemif->base = devm_ioremap_resource(dev, res); @@ -373,15 +372,23 @@ static int aemif_probe(struct platform_device *pdev) goto error; } - /* - * For every controller device node, there is a cs device node that - * describe the bus configuration parameters. This functions iterate - * over these nodes and update the cs data array. - */ - for_each_available_child_of_node(np, child_np) { - ret = of_aemif_parse_abus_config(pdev, child_np); - if (ret < 0) - goto error; + if (np) { + /* + * For every controller device node, there is a cs device node + * that describe the bus configuration parameters. This + * functions iterate over these nodes and update the cs data + * array. + */ + for_each_available_child_of_node(np, child_np) { + ret = of_aemif_parse_abus_config(pdev, child_np); + if (ret < 0) + goto error; + } + } else if (pdata && pdata->num_abus_data > 0) { + for (i = 0; i < pdata->num_abus_data; i++, aemif->num_cs++) { + aemif->cs_data[i].cs = pdata->abus_data[i].cs; + aemif_get_hw_params(pdev, i); + } } for (i = 0; i < aemif->num_cs; i++) { @@ -394,14 +401,25 @@ static int aemif_probe(struct platform_device *pdev) } /* - * Create a child devices explicitly from here to - * guarantee that the child will be probed after the AEMIF timing - * parameters are set. + * Create a child devices explicitly from here to guarantee that the + * child will be probed after the AEMIF timing parameters are set. */ - for_each_available_child_of_node(np, child_np) { - ret = of_platform_populate(child_np, NULL, dev_lookup, dev); - if (ret < 0) - goto error; + if (np) { + for_each_available_child_of_node(np, child_np) { + ret = of_platform_populate(child_np, NULL, + dev_lookup, dev); + if (ret < 0) + goto error; + } + } else { + for (i = 0; i < pdata->num_sub_devices; i++) { + pdata->sub_devices[i].dev.parent = dev; + ret = platform_device_register(&pdata->sub_devices[i]); + if (ret) { + dev_warn(dev, "Error register sub device %s\n", + pdata->sub_devices[i].name); + } + } } return 0; diff --git a/include/linux/platform_data/ti-aemif.h b/include/linux/platform_data/ti-aemif.h index ac72e115093c..e6407bafcbf8 100644 --- a/include/linux/platform_data/ti-aemif.h +++ b/include/linux/platform_data/ti-aemif.h @@ -16,8 +16,33 @@ #include +/** + * struct aemif_abus_data - Async bus configuration parameters. + * + * @cs - Chip-select number. + */ +struct aemif_abus_data { + u32 cs; +}; + +/** + * struct aemif_platform_data - Data to set up the TI aemif driver. + * + * @dev_lookup: of_dev_auxdata passed to of_platform_populate() for aemif + * subdevices. + * @cs_offset: Lowest allowed chip-select number. + * @abus_data: Array of async bus configuration entries. + * @num_abus_data: Number of abus entries. + * @sub_devices: Array of platform subdevices. + * @num_sub_devices: Number of subdevices. + */ struct aemif_platform_data { struct of_dev_auxdata *dev_lookup; + u32 cs_offset; + struct aemif_abus_data *abus_data; + size_t num_abus_data; + struct platform_device *sub_devices; + size_t num_sub_devices; }; #endif /* __TI_DAVINCI_AEMIF_DATA_H__ */ -- cgit v1.2.3 From f0316f93897c4c4e67278b175bfbfd3a95ba650a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 5 Dec 2015 18:41:28 +0000 Subject: drm/i2c: tda9950: add CEC driver Add a CEC driver for the TDA9950, which is a stand-alone I2C CEC device, but is also integrated into HDMI transceivers such as the TDA9989 and TDA19989. The TDA9950 contains a command processor which handles retransmissions and the low level bus protocol. The driver just has to read and write the messages, and handle error conditions. Reviewed-by: Hans Verkuil Signed-off-by: Russell King --- drivers/gpu/drm/i2c/Kconfig | 5 + drivers/gpu/drm/i2c/Makefile | 1 + drivers/gpu/drm/i2c/tda9950.c | 509 ++++++++++++++++++++++++++++++++++ include/linux/platform_data/tda9950.h | 16 ++ 4 files changed, 531 insertions(+) create mode 100644 drivers/gpu/drm/i2c/tda9950.c create mode 100644 include/linux/platform_data/tda9950.h (limited to 'include/linux/platform_data') diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index a6c92beb410a..3a232f5ff0a1 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig @@ -26,4 +26,9 @@ config DRM_I2C_NXP_TDA998X help Support for NXP Semiconductors TDA998X HDMI encoders. +config DRM_I2C_NXP_TDA9950 + tristate "NXP Semiconductors TDA9950/TDA998X HDMI CEC" + select CEC_NOTIFIER + select CEC_CORE + endmenu diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile index b20100c18ffb..a962f6f08568 100644 --- a/drivers/gpu/drm/i2c/Makefile +++ b/drivers/gpu/drm/i2c/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o tda998x-y := tda998x_drv.o obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o +obj-$(CONFIG_DRM_I2C_NXP_TDA9950) += tda9950.o diff --git a/drivers/gpu/drm/i2c/tda9950.c b/drivers/gpu/drm/i2c/tda9950.c new file mode 100644 index 000000000000..3f7396caad48 --- /dev/null +++ b/drivers/gpu/drm/i2c/tda9950.c @@ -0,0 +1,509 @@ +/* + * TDA9950 Consumer Electronics Control driver + * + * 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. + * + * The NXP TDA9950 implements the HDMI Consumer Electronics Control + * interface. The host interface is similar to a mailbox: the data + * registers starting at REG_CDR0 are written to send a command to the + * internal CPU, and replies are read from these registers. + * + * As the data registers represent a mailbox, they must be accessed + * as a single I2C transaction. See the TDA9950 data sheet for details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + REG_CSR = 0x00, + CSR_BUSY = BIT(7), + CSR_INT = BIT(6), + CSR_ERR = BIT(5), + + REG_CER = 0x01, + + REG_CVR = 0x02, + + REG_CCR = 0x03, + CCR_RESET = BIT(7), + CCR_ON = BIT(6), + + REG_ACKH = 0x04, + REG_ACKL = 0x05, + + REG_CCONR = 0x06, + CCONR_ENABLE_ERROR = BIT(4), + CCONR_RETRY_MASK = 7, + + REG_CDR0 = 0x07, + + CDR1_REQ = 0x00, + CDR1_CNF = 0x01, + CDR1_IND = 0x81, + CDR1_ERR = 0x82, + CDR1_IER = 0x83, + + CDR2_CNF_SUCCESS = 0x00, + CDR2_CNF_OFF_STATE = 0x80, + CDR2_CNF_BAD_REQ = 0x81, + CDR2_CNF_CEC_ACCESS = 0x82, + CDR2_CNF_ARB_ERROR = 0x83, + CDR2_CNF_BAD_TIMING = 0x84, + CDR2_CNF_NACK_ADDR = 0x85, + CDR2_CNF_NACK_DATA = 0x86, +}; + +struct tda9950_priv { + struct i2c_client *client; + struct device *hdmi; + struct cec_adapter *adap; + struct tda9950_glue *glue; + u16 addresses; + struct cec_msg rx_msg; + struct cec_notifier *notify; + bool open; +}; + +static int tda9950_write_range(struct i2c_client *client, u8 addr, u8 *p, int cnt) +{ + struct i2c_msg msg; + u8 buf[cnt + 1]; + int ret; + + buf[0] = addr; + memcpy(buf + 1, p, cnt); + + msg.addr = client->addr; + msg.flags = 0; + msg.len = cnt + 1; + msg.buf = buf; + + dev_dbg(&client->dev, "wr 0x%02x: %*ph\n", addr, cnt, p); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr); + return ret < 0 ? ret : 0; +} + +static void tda9950_write(struct i2c_client *client, u8 addr, u8 val) +{ + tda9950_write_range(client, addr, &val, 1); +} + +static int tda9950_read_range(struct i2c_client *client, u8 addr, u8 *p, int cnt) +{ + struct i2c_msg msg[2]; + int ret; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &addr; + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = cnt; + msg[1].buf = p; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) + dev_err(&client->dev, "Error %d reading from cec:0x%x\n", ret, addr); + + dev_dbg(&client->dev, "rd 0x%02x: %*ph\n", addr, cnt, p); + + return ret; +} + +static u8 tda9950_read(struct i2c_client *client, u8 addr) +{ + int ret; + u8 val; + + ret = tda9950_read_range(client, addr, &val, 1); + if (ret < 0) + val = 0; + + return val; +} + +static irqreturn_t tda9950_irq(int irq, void *data) +{ + struct tda9950_priv *priv = data; + unsigned int tx_status; + u8 csr, cconr, buf[19]; + u8 arb_lost_cnt, nack_cnt, err_cnt; + + if (!priv->open) + return IRQ_NONE; + + csr = tda9950_read(priv->client, REG_CSR); + if (!(csr & CSR_INT)) + return IRQ_NONE; + + cconr = tda9950_read(priv->client, REG_CCONR) & CCONR_RETRY_MASK; + + tda9950_read_range(priv->client, REG_CDR0, buf, sizeof(buf)); + + /* + * This should never happen: the data sheet says that there will + * always be a valid message if the interrupt line is asserted. + */ + if (buf[0] == 0) { + dev_warn(&priv->client->dev, "interrupt pending, but no message?\n"); + return IRQ_NONE; + } + + switch (buf[1]) { + case CDR1_CNF: /* transmit result */ + arb_lost_cnt = nack_cnt = err_cnt = 0; + switch (buf[2]) { + case CDR2_CNF_SUCCESS: + tx_status = CEC_TX_STATUS_OK; + break; + + case CDR2_CNF_ARB_ERROR: + tx_status = CEC_TX_STATUS_ARB_LOST; + arb_lost_cnt = cconr; + break; + + case CDR2_CNF_NACK_ADDR: + tx_status = CEC_TX_STATUS_NACK; + nack_cnt = cconr; + break; + + default: /* some other error, refer to TDA9950 docs */ + dev_err(&priv->client->dev, "CNF reply error 0x%02x\n", + buf[2]); + tx_status = CEC_TX_STATUS_ERROR; + err_cnt = cconr; + break; + } + /* TDA9950 executes all retries for us */ + tx_status |= CEC_TX_STATUS_MAX_RETRIES; + cec_transmit_done(priv->adap, tx_status, arb_lost_cnt, + nack_cnt, 0, err_cnt); + break; + + case CDR1_IND: + priv->rx_msg.len = buf[0] - 2; + if (priv->rx_msg.len > CEC_MAX_MSG_SIZE) + priv->rx_msg.len = CEC_MAX_MSG_SIZE; + + memcpy(priv->rx_msg.msg, buf + 2, priv->rx_msg.len); + cec_received_msg(priv->adap, &priv->rx_msg); + break; + + default: /* unknown */ + dev_err(&priv->client->dev, "unknown service id 0x%02x\n", + buf[1]); + break; + } + + return IRQ_HANDLED; +} + +static int tda9950_cec_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct tda9950_priv *priv = adap->priv; + u8 buf[CEC_MAX_MSG_SIZE + 2]; + + buf[0] = 2 + msg->len; + buf[1] = CDR1_REQ; + memcpy(buf + 2, msg->msg, msg->len); + + if (attempts > 5) + attempts = 5; + + tda9950_write(priv->client, REG_CCONR, attempts); + + return tda9950_write_range(priv->client, REG_CDR0, buf, 2 + msg->len); +} + +static int tda9950_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) +{ + struct tda9950_priv *priv = adap->priv; + u16 addresses; + u8 buf[2]; + + if (addr == CEC_LOG_ADDR_INVALID) + addresses = priv->addresses = 0; + else + addresses = priv->addresses |= BIT(addr); + + /* TDA9950 doesn't want address 15 set */ + addresses &= 0x7fff; + buf[0] = addresses >> 8; + buf[1] = addresses; + + return tda9950_write_range(priv->client, REG_ACKH, buf, 2); +} + +/* + * When operating as part of the TDA998x, we need additional handling + * to initialise and shut down the TDA9950 part of the device. These + * two hooks are provided to allow the TDA998x code to perform those + * activities. + */ +static int tda9950_glue_open(struct tda9950_priv *priv) +{ + int ret = 0; + + if (priv->glue && priv->glue->open) + ret = priv->glue->open(priv->glue->data); + + priv->open = true; + + return ret; +} + +static void tda9950_glue_release(struct tda9950_priv *priv) +{ + priv->open = false; + + if (priv->glue && priv->glue->release) + priv->glue->release(priv->glue->data); +} + +static int tda9950_open(struct tda9950_priv *priv) +{ + struct i2c_client *client = priv->client; + int ret; + + ret = tda9950_glue_open(priv); + if (ret) + return ret; + + /* Reset the TDA9950, and wait 250ms for it to recover */ + tda9950_write(client, REG_CCR, CCR_RESET); + msleep(250); + + tda9950_cec_adap_log_addr(priv->adap, CEC_LOG_ADDR_INVALID); + + /* Start the command processor */ + tda9950_write(client, REG_CCR, CCR_ON); + + return 0; +} + +static void tda9950_release(struct tda9950_priv *priv) +{ + struct i2c_client *client = priv->client; + int timeout = 50; + u8 csr; + + /* Stop the command processor */ + tda9950_write(client, REG_CCR, 0); + + /* Wait up to .5s for it to signal non-busy */ + do { + csr = tda9950_read(client, REG_CSR); + if (!(csr & CSR_BUSY) || --timeout) + break; + msleep(10); + } while (1); + + /* Warn the user that their IRQ may die if it's shared. */ + if (csr & CSR_BUSY) + dev_warn(&client->dev, "command processor failed to stop, irq%d may die (csr=0x%02x)\n", + client->irq, csr); + + tda9950_glue_release(priv); +} + +static int tda9950_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct tda9950_priv *priv = adap->priv; + + if (!enable) { + tda9950_release(priv); + return 0; + } else { + return tda9950_open(priv); + } +} + +static const struct cec_adap_ops tda9950_cec_ops = { + .adap_enable = tda9950_cec_adap_enable, + .adap_log_addr = tda9950_cec_adap_log_addr, + .adap_transmit = tda9950_cec_transmit, +}; + +/* + * When operating as part of the TDA998x, we need to claim additional + * resources. These two hooks permit the management of those resources. + */ +static void tda9950_devm_glue_exit(void *data) +{ + struct tda9950_glue *glue = data; + + if (glue && glue->exit) + glue->exit(glue->data); +} + +static int tda9950_devm_glue_init(struct device *dev, struct tda9950_glue *glue) +{ + int ret; + + if (glue && glue->init) { + ret = glue->init(glue->data); + if (ret) + return ret; + } + + ret = devm_add_action(dev, tda9950_devm_glue_exit, glue); + if (ret) + tda9950_devm_glue_exit(glue); + + return ret; +} + +static void tda9950_cec_del(void *data) +{ + struct tda9950_priv *priv = data; + + cec_delete_adapter(priv->adap); +} + +static int tda9950_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tda9950_glue *glue = client->dev.platform_data; + struct device *dev = &client->dev; + struct tda9950_priv *priv; + unsigned long irqflags; + int ret; + u8 cvr; + + /* + * We must have I2C functionality: our multi-byte accesses + * must be performed as a single contiguous transaction. + */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, + "adapter does not support I2C functionality\n"); + return -ENXIO; + } + + /* We must have an interrupt to be functional. */ + if (client->irq <= 0) { + dev_err(&client->dev, "driver requires an interrupt\n"); + return -ENXIO; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + priv->glue = glue; + + i2c_set_clientdata(client, priv); + + /* + * If we're part of a TDA998x, we want the class devices to be + * associated with the HDMI Tx so we have a tight relationship + * between the HDMI interface and the CEC interface. + */ + priv->hdmi = dev; + if (glue && glue->parent) + priv->hdmi = glue->parent; + + priv->adap = cec_allocate_adapter(&tda9950_cec_ops, priv, "tda9950", + CEC_CAP_DEFAULTS, + CEC_MAX_LOG_ADDRS); + if (IS_ERR(priv->adap)) + return PTR_ERR(priv->adap); + + ret = devm_add_action(dev, tda9950_cec_del, priv); + if (ret) { + cec_delete_adapter(priv->adap); + return ret; + } + + ret = tda9950_devm_glue_init(dev, glue); + if (ret) + return ret; + + ret = tda9950_glue_open(priv); + if (ret) + return ret; + + cvr = tda9950_read(client, REG_CVR); + + dev_info(&client->dev, + "TDA9950 CEC interface, hardware version %u.%u\n", + cvr >> 4, cvr & 15); + + tda9950_glue_release(priv); + + irqflags = IRQF_TRIGGER_FALLING; + if (glue) + irqflags = glue->irq_flags; + + ret = devm_request_threaded_irq(dev, client->irq, NULL, tda9950_irq, + irqflags | IRQF_SHARED | IRQF_ONESHOT, + dev_name(&client->dev), priv); + if (ret < 0) + return ret; + + priv->notify = cec_notifier_get(priv->hdmi); + if (!priv->notify) + return -ENOMEM; + + ret = cec_register_adapter(priv->adap, priv->hdmi); + if (ret < 0) { + cec_notifier_put(priv->notify); + return ret; + } + + /* + * CEC documentation says we must not call cec_delete_adapter + * after a successful call to cec_register_adapter(). + */ + devm_remove_action(dev, tda9950_cec_del, priv); + + cec_register_cec_notifier(priv->adap, priv->notify); + + return 0; +} + +static int tda9950_remove(struct i2c_client *client) +{ + struct tda9950_priv *priv = i2c_get_clientdata(client); + + cec_unregister_adapter(priv->adap); + cec_notifier_put(priv->notify); + + return 0; +} + +static struct i2c_device_id tda9950_ids[] = { + { "tda9950", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, tda9950_ids); + +static struct i2c_driver tda9950_driver = { + .probe = tda9950_probe, + .remove = tda9950_remove, + .driver = { + .name = "tda9950", + }, + .id_table = tda9950_ids, +}; + +module_i2c_driver(tda9950_driver); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("TDA9950/TDA998x Consumer Electronics Control Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/tda9950.h b/include/linux/platform_data/tda9950.h new file mode 100644 index 000000000000..c65efd461102 --- /dev/null +++ b/include/linux/platform_data/tda9950.h @@ -0,0 +1,16 @@ +#ifndef LINUX_PLATFORM_DATA_TDA9950_H +#define LINUX_PLATFORM_DATA_TDA9950_H + +struct device; + +struct tda9950_glue { + struct device *parent; + unsigned long irq_flags; + void *data; + int (*init)(void *); + void (*exit)(void *); + int (*open)(void *); + void (*release)(void *); +}; + +#endif -- cgit v1.2.3 From e7420c2d4495cbb9c14dd8bf8b3b4e5bdded6e20 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 16 Apr 2018 10:26:46 -0700 Subject: bus: ti-sysc: Tag some modules resource providers for noirq suspend Modules that provide resources for other modules need to be suspended and resumed in the noirq calls. Tag the resource providing modules. Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 77 +++++++++++++++++++++++++++++++++++ include/linux/platform_data/ti-sysc.h | 1 + 2 files changed, 78 insertions(+) (limited to 'include/linux/platform_data') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index f27b182384cd..1f90b91dbfae 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -665,6 +665,10 @@ static int sysc_suspend(struct device *dev) ddata = dev_get_drvdata(dev); + if (ddata->cfg.quirks & (SYSC_QUIRK_RESOURCE_PROVIDER | + SYSC_QUIRK_LEGACY_IDLE)) + return 0; + if (!ddata->enabled) return 0; @@ -678,6 +682,58 @@ static int sysc_resume(struct device *dev) struct sysc *ddata; ddata = dev_get_drvdata(dev); + + if (ddata->cfg.quirks & (SYSC_QUIRK_RESOURCE_PROVIDER | + SYSC_QUIRK_LEGACY_IDLE)) + return 0; + + if (ddata->needs_resume) { + dev_dbg(ddata->dev, "%s %s\n", __func__, + ddata->name ? ddata->name : ""); + + ddata->needs_resume = false; + + return sysc_runtime_resume(dev); + } + + return 0; +} + +static int sysc_noirq_suspend(struct device *dev) +{ + struct sysc *ddata; + + ddata = dev_get_drvdata(dev); + + if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE) + return 0; + + if (!(ddata->cfg.quirks & SYSC_QUIRK_RESOURCE_PROVIDER)) + return 0; + + if (!ddata->enabled) + return 0; + + dev_dbg(ddata->dev, "%s %s\n", __func__, + ddata->name ? ddata->name : ""); + + ddata->needs_resume = true; + + return sysc_runtime_suspend(dev); +} + +static int sysc_noirq_resume(struct device *dev) +{ + struct sysc *ddata; + + ddata = dev_get_drvdata(dev); + + if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE) + return 0; + + if (!(ddata->cfg.quirks & SYSC_QUIRK_RESOURCE_PROVIDER)) + return 0; + if (ddata->needs_resume) { ddata->needs_resume = false; @@ -690,6 +746,7 @@ static int sysc_resume(struct device *dev) static const struct dev_pm_ops sysc_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(sysc_suspend, sysc_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sysc_noirq_suspend, sysc_noirq_resume) SET_RUNTIME_PM_OPS(sysc_runtime_suspend, sysc_runtime_resume, NULL) @@ -721,6 +778,26 @@ struct sysc_revision_quirk { } static const struct sysc_revision_quirk sysc_revision_quirks[] = { + /* These need to use noirq_suspend */ + SYSC_QUIRK("control", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("mcspi", 0, 0, 0x10, -1, 0x40300a0b, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000100, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("ocp2scp", 0, 0, 0x10, 0x14, 0x50060005, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("padconf", 0, 0, 0x10, -1, 0x4fff0800, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("scm", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("scrm", 0, 0, -1, -1, 0x00000010, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + /* These drivers need to be fixed to not use pm_runtime_irq_safe() */ SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffffffff, SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_OPT_CLKS_IN_RESET), diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index 80ce28d40832..990aad477458 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -45,6 +45,7 @@ struct sysc_regbits { s8 emufree_shift; }; +#define SYSC_QUIRK_RESOURCE_PROVIDER BIT(9) #define SYSC_QUIRK_LEGACY_IDLE BIT(8) #define SYSC_QUIRK_RESET_STATUS BIT(7) #define SYSC_QUIRK_NO_IDLE_ON_INIT BIT(6) -- cgit v1.2.3 From bde1a3d84634f98151e3f748ab90865e9f544b10 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 30 Apr 2018 10:24:42 +0200 Subject: mtd: rawnand: davinci: store the core chipselect number in platform data We have the 'ti,davinci-chipselect' property in the device tree, but when using platform data the driver silently uses the id field of struct platform_device as the chipselect. This is confusing and we almost broke the nand support again recently after converting the platform to common clock framework (which changed the device id in the clock lookup - the problem is gone now that we no longer acquire the clock in the nand driver. This patch adds a new field - core_chipsel - to the platform_data. Subsequent patches will convert the platforms to using this new field. Acked-by: Boris Brezillon Signed-off-by: Bartosz Golaszewski Signed-off-by: Sekhar Nori --- include/linux/platform_data/mtd-davinci.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/mtd-davinci.h b/include/linux/platform_data/mtd-davinci.h index f1a2cf655bdb..1bbfa27cccb4 100644 --- a/include/linux/platform_data/mtd-davinci.h +++ b/include/linux/platform_data/mtd-davinci.h @@ -56,6 +56,16 @@ struct davinci_nand_pdata { /* platform_data */ uint32_t mask_ale; uint32_t mask_cle; + /* + * 0-indexed chip-select number of the asynchronous + * interface to which the NAND device has been connected. + * + * So, if you have NAND connected to CS3 of DA850, you + * will pass '1' here. Since the asynchronous interface + * on DA850 starts from CS2. + */ + uint32_t core_chipsel; + /* for packages using two chipselects */ uint32_t mask_chipsel; -- cgit v1.2.3 From c06c4d793584b965bf5fa3fb107f6279643574e2 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 10 May 2018 20:12:23 -0400 Subject: staging: iio: tsl2x7x/tsl2772: move out of staging Move the tsl2772 driver out of staging and into mainline. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 8 + drivers/iio/light/Makefile | 1 + drivers/iio/light/tsl2772.c | 1800 +++++++++++++++++++++++++++++++++ drivers/staging/iio/Kconfig | 1 - drivers/staging/iio/Makefile | 1 - drivers/staging/iio/light/Kconfig | 14 - drivers/staging/iio/light/Makefile | 5 - drivers/staging/iio/light/tsl2772.c | 1800 --------------------------------- drivers/staging/iio/light/tsl2772.h | 101 -- include/linux/platform_data/tsl2772.h | 101 ++ 10 files changed, 1910 insertions(+), 1922 deletions(-) create mode 100644 drivers/iio/light/tsl2772.c delete mode 100644 drivers/staging/iio/light/Kconfig delete mode 100644 drivers/staging/iio/light/Makefile delete mode 100644 drivers/staging/iio/light/tsl2772.c delete mode 100644 drivers/staging/iio/light/tsl2772.h create mode 100644 include/linux/platform_data/tsl2772.h (limited to 'include/linux/platform_data') diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 074e50657366..c7ef8d1862d6 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -409,6 +409,14 @@ config TSL2583 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. Access ALS data via iio, sysfs. +config TSL2772 + tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors" + depends on I2C + help + Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672, + tmd2672, tsl2772, tmd2772 devices. + Provides iio_events and direct access via sysfs. + config TSL4531 tristate "TAOS TSL4531 ambient light sensors" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index f1777036d4f8..80943af5d627 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3472) += tcs3472.o obj-$(CONFIG_TSL2583) += tsl2583.o +obj-$(CONFIG_TSL2772) += tsl2772.o obj-$(CONFIG_TSL4531) += tsl4531.o obj-$(CONFIG_US5182D) += us5182d.o obj-$(CONFIG_VCNL4000) += vcnl4000.o diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c new file mode 100644 index 000000000000..34d42a2504c9 --- /dev/null +++ b/drivers/iio/light/tsl2772.c @@ -0,0 +1,1800 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Device driver for monitoring ambient light intensity in (lux) and proximity + * detection (prox) within the TAOS TSL2571, TSL2671, TMD2671, TSL2771, TMD2771, + * TSL2572, TSL2672, TMD2672, TSL2772, and TMD2772 devices. + * + * Copyright (c) 2012, TAOS Corporation. + * Copyright (c) 2017-2018 Brian Masney + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Cal defs */ +#define PROX_STAT_CAL 0 +#define PROX_STAT_SAMP 1 +#define MAX_SAMPLES_CAL 200 + +/* TSL2772 Device ID */ +#define TRITON_ID 0x00 +#define SWORDFISH_ID 0x30 +#define HALIBUT_ID 0x20 + +/* Lux calculation constants */ +#define TSL2772_LUX_CALC_OVER_FLOW 65535 + +/* + * TAOS Register definitions - Note: depending on device, some of these register + * are not used and the register address is benign. + */ + +/* Register offsets */ +#define TSL2772_MAX_CONFIG_REG 16 + +/* Device Registers and Masks */ +#define TSL2772_CNTRL 0x00 +#define TSL2772_ALS_TIME 0X01 +#define TSL2772_PRX_TIME 0x02 +#define TSL2772_WAIT_TIME 0x03 +#define TSL2772_ALS_MINTHRESHLO 0X04 +#define TSL2772_ALS_MINTHRESHHI 0X05 +#define TSL2772_ALS_MAXTHRESHLO 0X06 +#define TSL2772_ALS_MAXTHRESHHI 0X07 +#define TSL2772_PRX_MINTHRESHLO 0X08 +#define TSL2772_PRX_MINTHRESHHI 0X09 +#define TSL2772_PRX_MAXTHRESHLO 0X0A +#define TSL2772_PRX_MAXTHRESHHI 0X0B +#define TSL2772_PERSISTENCE 0x0C +#define TSL2772_ALS_PRX_CONFIG 0x0D +#define TSL2772_PRX_COUNT 0x0E +#define TSL2772_GAIN 0x0F +#define TSL2772_NOTUSED 0x10 +#define TSL2772_REVID 0x11 +#define TSL2772_CHIPID 0x12 +#define TSL2772_STATUS 0x13 +#define TSL2772_ALS_CHAN0LO 0x14 +#define TSL2772_ALS_CHAN0HI 0x15 +#define TSL2772_ALS_CHAN1LO 0x16 +#define TSL2772_ALS_CHAN1HI 0x17 +#define TSL2772_PRX_LO 0x18 +#define TSL2772_PRX_HI 0x19 + +/* tsl2772 cmd reg masks */ +#define TSL2772_CMD_REG 0x80 +#define TSL2772_CMD_SPL_FN 0x60 +#define TSL2772_CMD_REPEAT_PROTO 0x00 +#define TSL2772_CMD_AUTOINC_PROTO 0x20 + +#define TSL2772_CMD_PROX_INT_CLR 0X05 +#define TSL2772_CMD_ALS_INT_CLR 0x06 +#define TSL2772_CMD_PROXALS_INT_CLR 0X07 + +/* tsl2772 cntrl reg masks */ +#define TSL2772_CNTL_ADC_ENBL 0x02 +#define TSL2772_CNTL_PWR_ON 0x01 + +/* tsl2772 status reg masks */ +#define TSL2772_STA_ADC_VALID 0x01 +#define TSL2772_STA_PRX_VALID 0x02 +#define TSL2772_STA_ADC_PRX_VALID (TSL2772_STA_ADC_VALID | \ + TSL2772_STA_PRX_VALID) +#define TSL2772_STA_ALS_INTR 0x10 +#define TSL2772_STA_PRX_INTR 0x20 + +/* tsl2772 cntrl reg masks */ +#define TSL2772_CNTL_REG_CLEAR 0x00 +#define TSL2772_CNTL_PROX_INT_ENBL 0X20 +#define TSL2772_CNTL_ALS_INT_ENBL 0X10 +#define TSL2772_CNTL_WAIT_TMR_ENBL 0X08 +#define TSL2772_CNTL_PROX_DET_ENBL 0X04 +#define TSL2772_CNTL_PWRON 0x01 +#define TSL2772_CNTL_ALSPON_ENBL 0x03 +#define TSL2772_CNTL_INTALSPON_ENBL 0x13 +#define TSL2772_CNTL_PROXPON_ENBL 0x0F +#define TSL2772_CNTL_INTPROXPON_ENBL 0x2F + +#define TSL2772_ALS_GAIN_TRIM_MIN 250 +#define TSL2772_ALS_GAIN_TRIM_MAX 4000 + +/* Device family members */ +enum { + tsl2571, + tsl2671, + tmd2671, + tsl2771, + tmd2771, + tsl2572, + tsl2672, + tmd2672, + tsl2772, + tmd2772 +}; + +enum { + TSL2772_CHIP_UNKNOWN = 0, + TSL2772_CHIP_WORKING = 1, + TSL2772_CHIP_SUSPENDED = 2 +}; + +/* Per-device data */ +struct tsl2772_als_info { + u16 als_ch0; + u16 als_ch1; + u16 lux; +}; + +struct tsl2772_chip_info { + int chan_table_elements; + struct iio_chan_spec channel_with_events[4]; + struct iio_chan_spec channel_without_events[4]; + const struct iio_info *info; +}; + +struct tsl2772_chip { + kernel_ulong_t id; + struct mutex prox_mutex; + struct mutex als_mutex; + struct i2c_client *client; + u16 prox_data; + struct tsl2772_als_info als_cur_info; + struct tsl2772_settings settings; + struct tsl2772_platform_data *pdata; + int als_gain_time_scale; + int als_saturation; + int tsl2772_chip_status; + u8 tsl2772_config[TSL2772_MAX_CONFIG_REG]; + const struct tsl2772_chip_info *chip_info; + const struct iio_info *info; + s64 event_timestamp; + /* + * This structure is intentionally large to accommodate + * updates via sysfs. + * Sized to 9 = max 8 segments + 1 termination segment + */ + struct tsl2772_lux tsl2772_device_lux[TSL2772_MAX_LUX_TABLE_SIZE]; +}; + +/* + * Different devices require different coefficents, and these numbers were + * derived from the 'Lux Equation' section of the various device datasheets. + * All of these coefficients assume a Glass Attenuation (GA) factor of 1. + * The coefficients are multiplied by 1000 to avoid floating point operations. + * The two rows in each table correspond to the Lux1 and Lux2 equations from + * the datasheets. + */ +static const struct tsl2772_lux tsl2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { + { 53000, 106000 }, + { 31800, 53000 }, + { 0, 0 }, +}; + +static const struct tsl2772_lux tmd2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { + { 24000, 48000 }, + { 14400, 24000 }, + { 0, 0 }, +}; + +static const struct tsl2772_lux tsl2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { + { 60000, 112200 }, + { 37800, 60000 }, + { 0, 0 }, +}; + +static const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { + { 20000, 35000 }, + { 12600, 20000 }, + { 0, 0 }, +}; + +static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { + [tsl2571] = tsl2x71_lux_table, + [tsl2671] = tsl2x71_lux_table, + [tmd2671] = tmd2x71_lux_table, + [tsl2771] = tsl2x71_lux_table, + [tmd2771] = tmd2x71_lux_table, + [tsl2572] = tsl2x72_lux_table, + [tsl2672] = tsl2x72_lux_table, + [tmd2672] = tmd2x72_lux_table, + [tsl2772] = tsl2x72_lux_table, + [tmd2772] = tmd2x72_lux_table, +}; + +static const struct tsl2772_settings tsl2772_default_settings = { + .als_time = 255, /* 2.72 / 2.73 ms */ + .als_gain = 0, + .prox_time = 255, /* 2.72 / 2.73 ms */ + .prox_gain = 0, + .wait_time = 255, + .als_prox_config = 0, + .als_gain_trim = 1000, + .als_cal_target = 150, + .als_persistence = 1, + .als_interrupt_en = false, + .als_thresh_low = 200, + .als_thresh_high = 256, + .prox_persistence = 1, + .prox_interrupt_en = false, + .prox_thres_low = 0, + .prox_thres_high = 512, + .prox_max_samples_cal = 30, + .prox_pulse_count = 8, + .prox_diode = TSL2772_DIODE1, + .prox_power = TSL2772_100_mA +}; + +static const s16 tsl2772_als_gain[] = { + 1, + 8, + 16, + 120 +}; + +static const s16 tsl2772_prox_gain[] = { + 1, + 2, + 4, + 8 +}; + +static const int tsl2772_int_time_avail[][6] = { + [tsl2571] = { 0, 2720, 0, 2720, 0, 696000 }, + [tsl2671] = { 0, 2720, 0, 2720, 0, 696000 }, + [tmd2671] = { 0, 2720, 0, 2720, 0, 696000 }, + [tsl2771] = { 0, 2720, 0, 2720, 0, 696000 }, + [tmd2771] = { 0, 2720, 0, 2720, 0, 696000 }, + [tsl2572] = { 0, 2730, 0, 2730, 0, 699000 }, + [tsl2672] = { 0, 2730, 0, 2730, 0, 699000 }, + [tmd2672] = { 0, 2730, 0, 2730, 0, 699000 }, + [tsl2772] = { 0, 2730, 0, 2730, 0, 699000 }, + [tmd2772] = { 0, 2730, 0, 2730, 0, 699000 }, +}; + +static int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 }; + +static int tsl2772_prox_calibscale_avail[] = { 1, 2, 4, 8 }; + +/* Channel variations */ +enum { + ALS, + PRX, + ALSPRX, + PRX2, + ALSPRX2, +}; + +static const u8 device_channel_config[] = { + [tsl2571] = ALS, + [tsl2671] = PRX, + [tmd2671] = PRX, + [tsl2771] = ALSPRX, + [tmd2771] = ALSPRX, + [tsl2572] = ALS, + [tsl2672] = PRX2, + [tmd2672] = PRX2, + [tsl2772] = ALSPRX2, + [tmd2772] = ALSPRX2 +}; + +static int tsl2772_read_status(struct tsl2772_chip *chip) +{ + int ret; + + ret = i2c_smbus_read_byte_data(chip->client, + TSL2772_CMD_REG | TSL2772_STATUS); + if (ret < 0) + dev_err(&chip->client->dev, + "%s: failed to read STATUS register: %d\n", __func__, + ret); + + return ret; +} + +static int tsl2772_write_control_reg(struct tsl2772_chip *chip, u8 data) +{ + int ret; + + ret = i2c_smbus_write_byte_data(chip->client, + TSL2772_CMD_REG | TSL2772_CNTRL, data); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to write to control register %x: %d\n", + __func__, data, ret); + } + + return ret; +} + +static int tsl2772_read_autoinc_regs(struct tsl2772_chip *chip, int lower_reg, + int upper_reg) +{ + u8 buf[2]; + int ret; + + ret = i2c_smbus_write_byte(chip->client, + TSL2772_CMD_REG | TSL2772_CMD_AUTOINC_PROTO | + lower_reg); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to enable auto increment protocol: %d\n", + __func__, ret); + return ret; + } + + ret = i2c_smbus_read_byte_data(chip->client, + TSL2772_CMD_REG | lower_reg); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to read from register %x: %d\n", __func__, + lower_reg, ret); + return ret; + } + buf[0] = ret; + + ret = i2c_smbus_read_byte_data(chip->client, + TSL2772_CMD_REG | upper_reg); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to read from register %x: %d\n", __func__, + upper_reg, ret); + return ret; + } + buf[1] = ret; + + ret = i2c_smbus_write_byte(chip->client, + TSL2772_CMD_REG | TSL2772_CMD_REPEAT_PROTO | + lower_reg); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to enable repeated byte protocol: %d\n", + __func__, ret); + return ret; + } + + return le16_to_cpup((const __le16 *)&buf[0]); +} + +/** + * tsl2772_get_lux() - Reads and calculates current lux value. + * @indio_dev: pointer to IIO device + * + * The raw ch0 and ch1 values of the ambient light sensed in the last + * integration cycle are read from the device. The raw values are multiplied + * by a device-specific scale factor, and divided by the integration time and + * device gain. The code supports multiple lux equations through the lux table + * coefficients. A lux gain trim is applied to each lux equation, and then the + * maximum lux within the interval 0..65535 is selected. + */ +static int tsl2772_get_lux(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + struct tsl2772_lux *p; + int max_lux, ret; + bool overflow; + + mutex_lock(&chip->als_mutex); + + if (chip->tsl2772_chip_status != TSL2772_CHIP_WORKING) { + dev_err(&chip->client->dev, "%s: device is not enabled\n", + __func__); + ret = -EBUSY; + goto out_unlock; + } + + ret = tsl2772_read_status(chip); + if (ret < 0) + goto out_unlock; + + if (!(ret & TSL2772_STA_ADC_VALID)) { + dev_err(&chip->client->dev, + "%s: data not valid yet\n", __func__); + ret = chip->als_cur_info.lux; /* return LAST VALUE */ + goto out_unlock; + } + + ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN0LO, + TSL2772_ALS_CHAN0HI); + if (ret < 0) + goto out_unlock; + chip->als_cur_info.als_ch0 = ret; + + ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN1LO, + TSL2772_ALS_CHAN1HI); + if (ret < 0) + goto out_unlock; + chip->als_cur_info.als_ch1 = ret; + + if (chip->als_cur_info.als_ch0 >= chip->als_saturation) { + max_lux = TSL2772_LUX_CALC_OVER_FLOW; + goto update_struct_with_max_lux; + } + + if (!chip->als_cur_info.als_ch0) { + /* have no data, so return LAST VALUE */ + ret = chip->als_cur_info.lux; + goto out_unlock; + } + + max_lux = 0; + overflow = false; + for (p = (struct tsl2772_lux *)chip->tsl2772_device_lux; p->ch0 != 0; + p++) { + int lux; + + lux = ((chip->als_cur_info.als_ch0 * p->ch0) - + (chip->als_cur_info.als_ch1 * p->ch1)) / + chip->als_gain_time_scale; + + /* + * The als_gain_trim can have a value within the range 250..4000 + * and is a multiplier for the lux. A trim of 1000 makes no + * changes to the lux, less than 1000 scales it down, and + * greater than 1000 scales it up. + */ + lux = (lux * chip->settings.als_gain_trim) / 1000; + + if (lux > TSL2772_LUX_CALC_OVER_FLOW) { + overflow = true; + continue; + } + + max_lux = max(max_lux, lux); + } + + if (overflow && max_lux == 0) + max_lux = TSL2772_LUX_CALC_OVER_FLOW; + +update_struct_with_max_lux: + chip->als_cur_info.lux = max_lux; + ret = max_lux; + +out_unlock: + mutex_unlock(&chip->als_mutex); + + return ret; +} + +/** + * tsl2772_get_prox() - Reads proximity data registers and updates + * chip->prox_data. + * + * @indio_dev: pointer to IIO device + */ +static int tsl2772_get_prox(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret; + + mutex_lock(&chip->prox_mutex); + + ret = tsl2772_read_status(chip); + if (ret < 0) + goto prox_poll_err; + + switch (chip->id) { + case tsl2571: + case tsl2671: + case tmd2671: + case tsl2771: + case tmd2771: + if (!(ret & TSL2772_STA_ADC_VALID)) { + ret = -EINVAL; + goto prox_poll_err; + } + break; + case tsl2572: + case tsl2672: + case tmd2672: + case tsl2772: + case tmd2772: + if (!(ret & TSL2772_STA_PRX_VALID)) { + ret = -EINVAL; + goto prox_poll_err; + } + break; + } + + ret = tsl2772_read_autoinc_regs(chip, TSL2772_PRX_LO, TSL2772_PRX_HI); + if (ret < 0) + goto prox_poll_err; + chip->prox_data = ret; + +prox_poll_err: + mutex_unlock(&chip->prox_mutex); + + return ret; +} + +/** + * tsl2772_defaults() - Populates the device nominal operating parameters + * with those provided by a 'platform' data struct or + * with prefined defaults. + * + * @chip: pointer to device structure. + */ +static void tsl2772_defaults(struct tsl2772_chip *chip) +{ + /* If Operational settings defined elsewhere.. */ + if (chip->pdata && chip->pdata->platform_default_settings) + memcpy(&chip->settings, chip->pdata->platform_default_settings, + sizeof(tsl2772_default_settings)); + else + memcpy(&chip->settings, &tsl2772_default_settings, + sizeof(tsl2772_default_settings)); + + /* Load up the proper lux table. */ + if (chip->pdata && chip->pdata->platform_lux_table[0].ch0 != 0) + memcpy(chip->tsl2772_device_lux, + chip->pdata->platform_lux_table, + sizeof(chip->pdata->platform_lux_table)); + else + memcpy(chip->tsl2772_device_lux, + tsl2772_default_lux_table_group[chip->id], + TSL2772_DEFAULT_TABLE_BYTES); +} + +/** + * tsl2772_als_calibrate() - Obtain single reading and calculate + * the als_gain_trim. + * + * @indio_dev: pointer to IIO device + */ +static int tsl2772_als_calibrate(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret, lux_val; + + ret = i2c_smbus_read_byte_data(chip->client, + TSL2772_CMD_REG | TSL2772_CNTRL); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to read from the CNTRL register\n", + __func__); + return ret; + } + + if ((ret & (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) + != (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) { + dev_err(&chip->client->dev, + "%s: Device is not powered on and/or ADC is not enabled\n", + __func__); + return -EINVAL; + } else if ((ret & TSL2772_STA_ADC_VALID) != TSL2772_STA_ADC_VALID) { + dev_err(&chip->client->dev, + "%s: The two ADC channels have not completed an integration cycle\n", + __func__); + return -ENODATA; + } + + lux_val = tsl2772_get_lux(indio_dev); + if (lux_val < 0) { + dev_err(&chip->client->dev, + "%s: failed to get lux\n", __func__); + return lux_val; + } + + ret = (chip->settings.als_cal_target * chip->settings.als_gain_trim) / + lux_val; + if (ret < TSL2772_ALS_GAIN_TRIM_MIN || ret > TSL2772_ALS_GAIN_TRIM_MAX) + return -ERANGE; + + chip->settings.als_gain_trim = ret; + + return ret; +} + +static int tsl2772_chip_on(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret, i, als_count, als_time_us; + u8 *dev_reg, reg_val; + + /* Non calculated parameters */ + chip->tsl2772_config[TSL2772_ALS_TIME] = chip->settings.als_time; + chip->tsl2772_config[TSL2772_PRX_TIME] = chip->settings.prox_time; + chip->tsl2772_config[TSL2772_WAIT_TIME] = chip->settings.wait_time; + chip->tsl2772_config[TSL2772_ALS_PRX_CONFIG] = + chip->settings.als_prox_config; + + chip->tsl2772_config[TSL2772_ALS_MINTHRESHLO] = + (chip->settings.als_thresh_low) & 0xFF; + chip->tsl2772_config[TSL2772_ALS_MINTHRESHHI] = + (chip->settings.als_thresh_low >> 8) & 0xFF; + chip->tsl2772_config[TSL2772_ALS_MAXTHRESHLO] = + (chip->settings.als_thresh_high) & 0xFF; + chip->tsl2772_config[TSL2772_ALS_MAXTHRESHHI] = + (chip->settings.als_thresh_high >> 8) & 0xFF; + chip->tsl2772_config[TSL2772_PERSISTENCE] = + (chip->settings.prox_persistence & 0xFF) << 4 | + (chip->settings.als_persistence & 0xFF); + + chip->tsl2772_config[TSL2772_PRX_COUNT] = + chip->settings.prox_pulse_count; + chip->tsl2772_config[TSL2772_PRX_MINTHRESHLO] = + (chip->settings.prox_thres_low) & 0xFF; + chip->tsl2772_config[TSL2772_PRX_MINTHRESHHI] = + (chip->settings.prox_thres_low >> 8) & 0xFF; + chip->tsl2772_config[TSL2772_PRX_MAXTHRESHLO] = + (chip->settings.prox_thres_high) & 0xFF; + chip->tsl2772_config[TSL2772_PRX_MAXTHRESHHI] = + (chip->settings.prox_thres_high >> 8) & 0xFF; + + /* and make sure we're not already on */ + if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { + /* if forcing a register update - turn off, then on */ + dev_info(&chip->client->dev, "device is already enabled\n"); + return -EINVAL; + } + + /* Set the gain based on tsl2772_settings struct */ + chip->tsl2772_config[TSL2772_GAIN] = + (chip->settings.als_gain & 0xFF) | + ((chip->settings.prox_gain & 0xFF) << 2) | + (chip->settings.prox_diode << 4) | + (chip->settings.prox_power << 6); + + /* set chip time scaling and saturation */ + als_count = 256 - chip->settings.als_time; + als_time_us = als_count * tsl2772_int_time_avail[chip->id][3]; + chip->als_saturation = als_count * 768; /* 75% of full scale */ + chip->als_gain_time_scale = als_time_us * + tsl2772_als_gain[chip->settings.als_gain]; + + /* + * TSL2772 Specific power-on / adc enable sequence + * Power on the device 1st. + */ + ret = tsl2772_write_control_reg(chip, TSL2772_CNTL_PWR_ON); + if (ret < 0) + return ret; + + /* + * Use the following shadow copy for our delay before enabling ADC. + * Write all the registers. + */ + for (i = 0, dev_reg = chip->tsl2772_config; + i < TSL2772_MAX_CONFIG_REG; i++) { + int reg = TSL2772_CMD_REG + i; + + ret = i2c_smbus_write_byte_data(chip->client, reg, + *dev_reg++); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to write to register %x: %d\n", + __func__, reg, ret); + return ret; + } + } + + /* Power-on settling time */ + usleep_range(3000, 3500); + + reg_val = TSL2772_CNTL_PWR_ON | TSL2772_CNTL_ADC_ENBL | + TSL2772_CNTL_PROX_DET_ENBL; + if (chip->settings.als_interrupt_en) + reg_val |= TSL2772_CNTL_ALS_INT_ENBL; + if (chip->settings.prox_interrupt_en) + reg_val |= TSL2772_CNTL_PROX_INT_ENBL; + + ret = tsl2772_write_control_reg(chip, reg_val); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte(chip->client, + TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | + TSL2772_CMD_PROXALS_INT_CLR); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to clear interrupt status: %d\n", + __func__, ret); + return ret; + } + + chip->tsl2772_chip_status = TSL2772_CHIP_WORKING; + + return ret; +} + +static int tsl2772_chip_off(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + /* turn device off */ + chip->tsl2772_chip_status = TSL2772_CHIP_SUSPENDED; + return tsl2772_write_control_reg(chip, 0x00); +} + +/** + * tsl2772_invoke_change - power cycle the device to implement the user + * parameters + * @indio_dev: pointer to IIO device + * + * Obtain and lock both ALS and PROX resources, determine and save device state + * (On/Off), cycle device to implement updated parameter, put device back into + * proper state, and unlock resource. + */ +static int tsl2772_invoke_change(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int device_status = chip->tsl2772_chip_status; + int ret; + + mutex_lock(&chip->als_mutex); + mutex_lock(&chip->prox_mutex); + + if (device_status == TSL2772_CHIP_WORKING) { + ret = tsl2772_chip_off(indio_dev); + if (ret < 0) + goto unlock; + } + + ret = tsl2772_chip_on(indio_dev); + +unlock: + mutex_unlock(&chip->prox_mutex); + mutex_unlock(&chip->als_mutex); + + return ret; +} + +static int tsl2772_prox_cal(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int prox_history[MAX_SAMPLES_CAL + 1]; + int i, ret, mean, max, sample_sum; + + if (chip->settings.prox_max_samples_cal < 1 || + chip->settings.prox_max_samples_cal > MAX_SAMPLES_CAL) + return -EINVAL; + + for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { + usleep_range(15000, 17500); + ret = tsl2772_get_prox(indio_dev); + if (ret < 0) + return ret; + + prox_history[i] = chip->prox_data; + } + + sample_sum = 0; + max = INT_MIN; + for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { + sample_sum += prox_history[i]; + max = max(max, prox_history[i]); + } + mean = sample_sum / chip->settings.prox_max_samples_cal; + + chip->settings.prox_thres_high = (max << 1) - mean; + + return tsl2772_invoke_change(indio_dev); +} + +static int tsl2772_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + if (chan->type == IIO_INTENSITY) { + *length = ARRAY_SIZE(tsl2772_int_calibscale_avail); + *vals = tsl2772_int_calibscale_avail; + } else { + *length = ARRAY_SIZE(tsl2772_prox_calibscale_avail); + *vals = tsl2772_prox_calibscale_avail; + } + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_INT_TIME: + *length = ARRAY_SIZE(tsl2772_int_time_avail[chip->id]); + *vals = tsl2772_int_time_avail[chip->id]; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_RANGE; + } + + return -EINVAL; +} + +static ssize_t in_illuminance0_target_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); + + return snprintf(buf, PAGE_SIZE, "%d\n", chip->settings.als_cal_target); +} + +static ssize_t in_illuminance0_target_input_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct tsl2772_chip *chip = iio_priv(indio_dev); + u16 value; + int ret; + + if (kstrtou16(buf, 0, &value)) + return -EINVAL; + + chip->settings.als_cal_target = value; + ret = tsl2772_invoke_change(indio_dev); + if (ret < 0) + return ret; + + return len; +} + +static ssize_t in_illuminance0_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + bool value; + int ret; + + if (kstrtobool(buf, &value) || !value) + return -EINVAL; + + ret = tsl2772_als_calibrate(indio_dev); + if (ret < 0) + return ret; + + ret = tsl2772_invoke_change(indio_dev); + if (ret < 0) + return ret; + + return len; +} + +static ssize_t in_illuminance0_lux_table_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); + int i = 0; + int offset = 0; + + while (i < TSL2772_MAX_LUX_TABLE_SIZE) { + offset += snprintf(buf + offset, PAGE_SIZE, "%u,%u,", + chip->tsl2772_device_lux[i].ch0, + chip->tsl2772_device_lux[i].ch1); + if (chip->tsl2772_device_lux[i].ch0 == 0) { + /* + * We just printed the first "0" entry. + * Now get rid of the extra "," and break. + */ + offset--; + break; + } + i++; + } + + offset += snprintf(buf + offset, PAGE_SIZE, "\n"); + return offset; +} + +static ssize_t in_illuminance0_lux_table_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct tsl2772_chip *chip = iio_priv(indio_dev); + int value[ARRAY_SIZE(chip->tsl2772_device_lux) * 2 + 1]; + int n, ret; + + get_options(buf, ARRAY_SIZE(value), value); + + /* + * We now have an array of ints starting at value[1], and + * enumerated by value[0]. + * We expect each group of two ints to be one table entry, + * and the last table entry is all 0. + */ + n = value[0]; + if ((n % 2) || n < 4 || + n > ((ARRAY_SIZE(chip->tsl2772_device_lux) - 1) * 2)) + return -EINVAL; + + if ((value[(n - 1)] | value[n]) != 0) + return -EINVAL; + + if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { + ret = tsl2772_chip_off(indio_dev); + if (ret < 0) + return ret; + } + + /* Zero out the table */ + memset(chip->tsl2772_device_lux, 0, sizeof(chip->tsl2772_device_lux)); + memcpy(chip->tsl2772_device_lux, &value[1], (value[0] * 4)); + + ret = tsl2772_invoke_change(indio_dev); + if (ret < 0) + return ret; + + return len; +} + +static ssize_t in_proximity0_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + bool value; + int ret; + + if (kstrtobool(buf, &value) || !value) + return -EINVAL; + + ret = tsl2772_prox_cal(indio_dev); + if (ret < 0) + return ret; + + ret = tsl2772_invoke_change(indio_dev); + if (ret < 0) + return ret; + + return len; +} + +static int tsl2772_read_interrupt_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + if (chan->type == IIO_INTENSITY) + return chip->settings.als_interrupt_en; + else + return chip->settings.prox_interrupt_en; +} + +static int tsl2772_write_interrupt_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int val) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + if (chan->type == IIO_INTENSITY) + chip->settings.als_interrupt_en = val ? true : false; + else + chip->settings.prox_interrupt_en = val ? true : false; + + return tsl2772_invoke_change(indio_dev); +} + +static int tsl2772_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret = -EINVAL, count, persistence; + u8 time; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (chan->type == IIO_INTENSITY) { + switch (dir) { + case IIO_EV_DIR_RISING: + chip->settings.als_thresh_high = val; + ret = 0; + break; + case IIO_EV_DIR_FALLING: + chip->settings.als_thresh_low = val; + ret = 0; + break; + default: + break; + } + } else { + switch (dir) { + case IIO_EV_DIR_RISING: + chip->settings.prox_thres_high = val; + ret = 0; + break; + case IIO_EV_DIR_FALLING: + chip->settings.prox_thres_low = val; + ret = 0; + break; + default: + break; + } + } + break; + case IIO_EV_INFO_PERIOD: + if (chan->type == IIO_INTENSITY) + time = chip->settings.als_time; + else + time = chip->settings.prox_time; + + count = 256 - time; + persistence = ((val * 1000000) + val2) / + (count * tsl2772_int_time_avail[chip->id][3]); + + if (chan->type == IIO_INTENSITY) { + /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ + if (persistence > 3) + persistence = (persistence / 5) + 3; + + chip->settings.als_persistence = persistence; + } else { + chip->settings.prox_persistence = persistence; + } + + ret = 0; + break; + default: + break; + } + + if (ret < 0) + return ret; + + return tsl2772_invoke_change(indio_dev); +} + +static int tsl2772_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int filter_delay, persistence; + u8 time; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (chan->type == IIO_INTENSITY) { + switch (dir) { + case IIO_EV_DIR_RISING: + *val = chip->settings.als_thresh_high; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + *val = chip->settings.als_thresh_low; + return IIO_VAL_INT; + default: + return -EINVAL; + } + } else { + switch (dir) { + case IIO_EV_DIR_RISING: + *val = chip->settings.prox_thres_high; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + *val = chip->settings.prox_thres_low; + return IIO_VAL_INT; + default: + return -EINVAL; + } + } + break; + case IIO_EV_INFO_PERIOD: + if (chan->type == IIO_INTENSITY) { + time = chip->settings.als_time; + persistence = chip->settings.als_persistence; + + /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ + if (persistence > 3) + persistence = (persistence - 3) * 5; + } else { + time = chip->settings.prox_time; + persistence = chip->settings.prox_persistence; + } + + filter_delay = persistence * (256 - time) * + tsl2772_int_time_avail[chip->id][3]; + + *val = filter_delay / 1000000; + *val2 = filter_delay % 1000000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int tsl2772_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_LIGHT: + tsl2772_get_lux(indio_dev); + *val = chip->als_cur_info.lux; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_INTENSITY: + tsl2772_get_lux(indio_dev); + if (chan->channel == 0) + *val = chip->als_cur_info.als_ch0; + else + *val = chip->als_cur_info.als_ch1; + return IIO_VAL_INT; + case IIO_PROXIMITY: + tsl2772_get_prox(indio_dev); + *val = chip->prox_data; + return IIO_VAL_INT; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_CALIBSCALE: + if (chan->type == IIO_LIGHT) + *val = tsl2772_als_gain[chip->settings.als_gain]; + else + *val = tsl2772_prox_gain[chip->settings.prox_gain]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + *val = chip->settings.als_gain_trim; + return IIO_VAL_INT; + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + *val2 = (256 - chip->settings.als_time) * + tsl2772_int_time_avail[chip->id][3]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int tsl2772_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + if (chan->type == IIO_INTENSITY) { + switch (val) { + case 1: + chip->settings.als_gain = 0; + break; + case 8: + chip->settings.als_gain = 1; + break; + case 16: + chip->settings.als_gain = 2; + break; + case 120: + chip->settings.als_gain = 3; + break; + default: + return -EINVAL; + } + } else { + switch (val) { + case 1: + chip->settings.prox_gain = 0; + break; + case 2: + chip->settings.prox_gain = 1; + break; + case 4: + chip->settings.prox_gain = 2; + break; + case 8: + chip->settings.prox_gain = 3; + break; + default: + return -EINVAL; + } + } + break; + case IIO_CHAN_INFO_CALIBBIAS: + if (val < TSL2772_ALS_GAIN_TRIM_MIN || + val > TSL2772_ALS_GAIN_TRIM_MAX) + return -EINVAL; + + chip->settings.als_gain_trim = val; + break; + case IIO_CHAN_INFO_INT_TIME: + if (val != 0 || val2 < tsl2772_int_time_avail[chip->id][1] || + val2 > tsl2772_int_time_avail[chip->id][5]) + return -EINVAL; + + chip->settings.als_time = 256 - + (val2 / tsl2772_int_time_avail[chip->id][3]); + break; + default: + return -EINVAL; + } + + return tsl2772_invoke_change(indio_dev); +} + +static DEVICE_ATTR_RW(in_illuminance0_target_input); + +static DEVICE_ATTR_WO(in_illuminance0_calibrate); + +static DEVICE_ATTR_WO(in_proximity0_calibrate); + +static DEVICE_ATTR_RW(in_illuminance0_lux_table); + +/* Use the default register values to identify the Taos device */ +static int tsl2772_device_id_verif(int id, int target) +{ + switch (target) { + case tsl2571: + case tsl2671: + case tsl2771: + return (id & 0xf0) == TRITON_ID; + case tmd2671: + case tmd2771: + return (id & 0xf0) == HALIBUT_ID; + case tsl2572: + case tsl2672: + case tmd2672: + case tsl2772: + case tmd2772: + return (id & 0xf0) == SWORDFISH_ID; + } + + return -EINVAL; +} + +static irqreturn_t tsl2772_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct tsl2772_chip *chip = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(indio_dev); + int ret; + + ret = tsl2772_read_status(chip); + if (ret < 0) + return IRQ_HANDLED; + + /* What type of interrupt do we need to process */ + if (ret & TSL2772_STA_PRX_INTR) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + timestamp); + } + + if (ret & TSL2772_STA_ALS_INTR) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + timestamp); + } + + ret = i2c_smbus_write_byte(chip->client, + TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | + TSL2772_CMD_PROXALS_INT_CLR); + if (ret < 0) + dev_err(&chip->client->dev, + "%s: failed to clear interrupt status: %d\n", + __func__, ret); + + return IRQ_HANDLED; +} + +static struct attribute *tsl2772_ALS_device_attrs[] = { + &dev_attr_in_illuminance0_target_input.attr, + &dev_attr_in_illuminance0_calibrate.attr, + &dev_attr_in_illuminance0_lux_table.attr, + NULL +}; + +static struct attribute *tsl2772_PRX_device_attrs[] = { + &dev_attr_in_proximity0_calibrate.attr, + NULL +}; + +static struct attribute *tsl2772_ALSPRX_device_attrs[] = { + &dev_attr_in_illuminance0_target_input.attr, + &dev_attr_in_illuminance0_calibrate.attr, + &dev_attr_in_illuminance0_lux_table.attr, + NULL +}; + +static struct attribute *tsl2772_PRX2_device_attrs[] = { + &dev_attr_in_proximity0_calibrate.attr, + NULL +}; + +static struct attribute *tsl2772_ALSPRX2_device_attrs[] = { + &dev_attr_in_illuminance0_target_input.attr, + &dev_attr_in_illuminance0_calibrate.attr, + &dev_attr_in_illuminance0_lux_table.attr, + &dev_attr_in_proximity0_calibrate.attr, + NULL +}; + +static const struct attribute_group tsl2772_device_attr_group_tbl[] = { + [ALS] = { + .attrs = tsl2772_ALS_device_attrs, + }, + [PRX] = { + .attrs = tsl2772_PRX_device_attrs, + }, + [ALSPRX] = { + .attrs = tsl2772_ALSPRX_device_attrs, + }, + [PRX2] = { + .attrs = tsl2772_PRX2_device_attrs, + }, + [ALSPRX2] = { + .attrs = tsl2772_ALSPRX2_device_attrs, + }, +}; + +#define TSL2772_DEVICE_INFO(type)[type] = \ + { \ + .attrs = &tsl2772_device_attr_group_tbl[type], \ + .read_raw = &tsl2772_read_raw, \ + .read_avail = &tsl2772_read_avail, \ + .write_raw = &tsl2772_write_raw, \ + .read_event_value = &tsl2772_read_event_value, \ + .write_event_value = &tsl2772_write_event_value, \ + .read_event_config = &tsl2772_read_interrupt_config, \ + .write_event_config = &tsl2772_write_interrupt_config, \ + } + +static const struct iio_info tsl2772_device_info[] = { + TSL2772_DEVICE_INFO(ALS), + TSL2772_DEVICE_INFO(PRX), + TSL2772_DEVICE_INFO(ALSPRX), + TSL2772_DEVICE_INFO(PRX2), + TSL2772_DEVICE_INFO(ALSPRX2), +}; + +static const struct iio_event_spec tsl2772_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct tsl2772_chip_info tsl2772_chip_info_tbl[] = { + [ALS] = { + .channel_with_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + }, + }, + .channel_without_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + }, + }, + .chan_table_elements = 3, + .info = &tsl2772_device_info[ALS], + }, + [PRX] = { + .channel_with_events = { + { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, + }, + .channel_without_events = { + { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, + }, + .chan_table_elements = 1, + .info = &tsl2772_device_info[PRX], + }, + [ALSPRX] = { + .channel_with_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, + }, + .channel_without_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, + }, + .chan_table_elements = 4, + .info = &tsl2772_device_info[ALSPRX], + }, + [PRX2] = { + .channel_with_events = { + { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_CALIBSCALE), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, + }, + .channel_without_events = { + { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_CALIBSCALE), + }, + }, + .chan_table_elements = 1, + .info = &tsl2772_device_info[PRX2], + }, + [ALSPRX2] = { + .channel_with_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_CALIBSCALE), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, + }, + .channel_without_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_CALIBSCALE), + }, + }, + .chan_table_elements = 4, + .info = &tsl2772_device_info[ALSPRX2], + }, +}; + +static int tsl2772_probe(struct i2c_client *clientp, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct tsl2772_chip *chip; + int ret; + + indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + chip->client = clientp; + i2c_set_clientdata(clientp, indio_dev); + + ret = i2c_smbus_read_byte_data(chip->client, + TSL2772_CMD_REG | TSL2772_CHIPID); + if (ret < 0) + return ret; + + if (tsl2772_device_id_verif(ret, id->driver_data) <= 0) { + dev_info(&chip->client->dev, + "%s: i2c device found does not match expected id\n", + __func__); + return -EINVAL; + } + + ret = i2c_smbus_write_byte(clientp, TSL2772_CMD_REG | TSL2772_CNTRL); + if (ret < 0) { + dev_err(&clientp->dev, + "%s: Failed to write to CMD register: %d\n", + __func__, ret); + return ret; + } + + mutex_init(&chip->als_mutex); + mutex_init(&chip->prox_mutex); + + chip->tsl2772_chip_status = TSL2772_CHIP_UNKNOWN; + chip->pdata = dev_get_platdata(&clientp->dev); + chip->id = id->driver_data; + chip->chip_info = + &tsl2772_chip_info_tbl[device_channel_config[id->driver_data]]; + + indio_dev->info = chip->chip_info->info; + indio_dev->dev.parent = &clientp->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = chip->client->name; + indio_dev->num_channels = chip->chip_info->chan_table_elements; + + if (clientp->irq) { + indio_dev->channels = chip->chip_info->channel_with_events; + + ret = devm_request_threaded_irq(&clientp->dev, clientp->irq, + NULL, + &tsl2772_event_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "TSL2772_event", + indio_dev); + if (ret) { + dev_err(&clientp->dev, + "%s: irq request failed\n", __func__); + return ret; + } + } else { + indio_dev->channels = chip->chip_info->channel_without_events; + } + + tsl2772_defaults(chip); + ret = tsl2772_chip_on(indio_dev); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) { + tsl2772_chip_off(indio_dev); + dev_err(&clientp->dev, + "%s: iio registration failed\n", __func__); + return ret; + } + + return 0; +} + +static int tsl2772_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return tsl2772_chip_off(indio_dev); +} + +static int tsl2772_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return tsl2772_chip_on(indio_dev); +} + +static int tsl2772_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + tsl2772_chip_off(indio_dev); + + iio_device_unregister(indio_dev); + + return 0; +} + +static const struct i2c_device_id tsl2772_idtable[] = { + { "tsl2571", tsl2571 }, + { "tsl2671", tsl2671 }, + { "tmd2671", tmd2671 }, + { "tsl2771", tsl2771 }, + { "tmd2771", tmd2771 }, + { "tsl2572", tsl2572 }, + { "tsl2672", tsl2672 }, + { "tmd2672", tmd2672 }, + { "tsl2772", tsl2772 }, + { "tmd2772", tmd2772 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tsl2772_idtable); + +static const struct of_device_id tsl2772_of_match[] = { + { .compatible = "amstaos,tsl2571" }, + { .compatible = "amstaos,tsl2671" }, + { .compatible = "amstaos,tmd2671" }, + { .compatible = "amstaos,tsl2771" }, + { .compatible = "amstaos,tmd2771" }, + { .compatible = "amstaos,tsl2572" }, + { .compatible = "amstaos,tsl2672" }, + { .compatible = "amstaos,tmd2672" }, + { .compatible = "amstaos,tsl2772" }, + { .compatible = "amstaos,tmd2772" }, + {} +}; +MODULE_DEVICE_TABLE(of, tsl2772_of_match); + +static const struct dev_pm_ops tsl2772_pm_ops = { + .suspend = tsl2772_suspend, + .resume = tsl2772_resume, +}; + +static struct i2c_driver tsl2772_driver = { + .driver = { + .name = "tsl2772", + .of_match_table = tsl2772_of_match, + .pm = &tsl2772_pm_ops, + }, + .id_table = tsl2772_idtable, + .probe = tsl2772_probe, + .remove = tsl2772_remove, +}; + +module_i2c_driver(tsl2772_driver); + +MODULE_AUTHOR("J. August Brenner "); +MODULE_AUTHOR("Brian Masney "); +MODULE_DESCRIPTION("TAOS tsl2772 ambient and proximity light sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index bd9445956511..aee2335a25a1 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -11,7 +11,6 @@ source "drivers/staging/iio/cdc/Kconfig" source "drivers/staging/iio/frequency/Kconfig" source "drivers/staging/iio/gyro/Kconfig" source "drivers/staging/iio/impedance-analyzer/Kconfig" -source "drivers/staging/iio/light/Kconfig" source "drivers/staging/iio/meter/Kconfig" source "drivers/staging/iio/resolver/Kconfig" diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index e99a375c07b9..c28d657497de 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -10,6 +10,5 @@ obj-y += cdc/ obj-y += frequency/ obj-y += gyro/ obj-y += impedance-analyzer/ -obj-y += light/ obj-y += meter/ obj-y += resolver/ diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig deleted file mode 100644 index dfa37386ad2c..000000000000 --- a/drivers/staging/iio/light/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -# -# Light sensors -# -menu "Light sensors" - -config TSL2772 - tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors" - depends on I2C - help - Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672, - tmd2672, tsl2772, tmd2772 devices. - Provides iio_events and direct access via sysfs. - -endmenu diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile deleted file mode 100644 index e7e77a11f02a..000000000000 --- a/drivers/staging/iio/light/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for industrial I/O Light sensors -# - -obj-$(CONFIG_TSL2772) += tsl2772.o diff --git a/drivers/staging/iio/light/tsl2772.c b/drivers/staging/iio/light/tsl2772.c deleted file mode 100644 index a59bf39c28d4..000000000000 --- a/drivers/staging/iio/light/tsl2772.c +++ /dev/null @@ -1,1800 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Device driver for monitoring ambient light intensity in (lux) and proximity - * detection (prox) within the TAOS TSL2571, TSL2671, TMD2671, TSL2771, TMD2771, - * TSL2572, TSL2672, TMD2672, TSL2772, and TMD2772 devices. - * - * Copyright (c) 2012, TAOS Corporation. - * Copyright (c) 2017-2018 Brian Masney - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "tsl2772.h" - -/* Cal defs */ -#define PROX_STAT_CAL 0 -#define PROX_STAT_SAMP 1 -#define MAX_SAMPLES_CAL 200 - -/* TSL2772 Device ID */ -#define TRITON_ID 0x00 -#define SWORDFISH_ID 0x30 -#define HALIBUT_ID 0x20 - -/* Lux calculation constants */ -#define TSL2772_LUX_CALC_OVER_FLOW 65535 - -/* - * TAOS Register definitions - Note: depending on device, some of these register - * are not used and the register address is benign. - */ - -/* Register offsets */ -#define TSL2772_MAX_CONFIG_REG 16 - -/* Device Registers and Masks */ -#define TSL2772_CNTRL 0x00 -#define TSL2772_ALS_TIME 0X01 -#define TSL2772_PRX_TIME 0x02 -#define TSL2772_WAIT_TIME 0x03 -#define TSL2772_ALS_MINTHRESHLO 0X04 -#define TSL2772_ALS_MINTHRESHHI 0X05 -#define TSL2772_ALS_MAXTHRESHLO 0X06 -#define TSL2772_ALS_MAXTHRESHHI 0X07 -#define TSL2772_PRX_MINTHRESHLO 0X08 -#define TSL2772_PRX_MINTHRESHHI 0X09 -#define TSL2772_PRX_MAXTHRESHLO 0X0A -#define TSL2772_PRX_MAXTHRESHHI 0X0B -#define TSL2772_PERSISTENCE 0x0C -#define TSL2772_ALS_PRX_CONFIG 0x0D -#define TSL2772_PRX_COUNT 0x0E -#define TSL2772_GAIN 0x0F -#define TSL2772_NOTUSED 0x10 -#define TSL2772_REVID 0x11 -#define TSL2772_CHIPID 0x12 -#define TSL2772_STATUS 0x13 -#define TSL2772_ALS_CHAN0LO 0x14 -#define TSL2772_ALS_CHAN0HI 0x15 -#define TSL2772_ALS_CHAN1LO 0x16 -#define TSL2772_ALS_CHAN1HI 0x17 -#define TSL2772_PRX_LO 0x18 -#define TSL2772_PRX_HI 0x19 - -/* tsl2772 cmd reg masks */ -#define TSL2772_CMD_REG 0x80 -#define TSL2772_CMD_SPL_FN 0x60 -#define TSL2772_CMD_REPEAT_PROTO 0x00 -#define TSL2772_CMD_AUTOINC_PROTO 0x20 - -#define TSL2772_CMD_PROX_INT_CLR 0X05 -#define TSL2772_CMD_ALS_INT_CLR 0x06 -#define TSL2772_CMD_PROXALS_INT_CLR 0X07 - -/* tsl2772 cntrl reg masks */ -#define TSL2772_CNTL_ADC_ENBL 0x02 -#define TSL2772_CNTL_PWR_ON 0x01 - -/* tsl2772 status reg masks */ -#define TSL2772_STA_ADC_VALID 0x01 -#define TSL2772_STA_PRX_VALID 0x02 -#define TSL2772_STA_ADC_PRX_VALID (TSL2772_STA_ADC_VALID | \ - TSL2772_STA_PRX_VALID) -#define TSL2772_STA_ALS_INTR 0x10 -#define TSL2772_STA_PRX_INTR 0x20 - -/* tsl2772 cntrl reg masks */ -#define TSL2772_CNTL_REG_CLEAR 0x00 -#define TSL2772_CNTL_PROX_INT_ENBL 0X20 -#define TSL2772_CNTL_ALS_INT_ENBL 0X10 -#define TSL2772_CNTL_WAIT_TMR_ENBL 0X08 -#define TSL2772_CNTL_PROX_DET_ENBL 0X04 -#define TSL2772_CNTL_PWRON 0x01 -#define TSL2772_CNTL_ALSPON_ENBL 0x03 -#define TSL2772_CNTL_INTALSPON_ENBL 0x13 -#define TSL2772_CNTL_PROXPON_ENBL 0x0F -#define TSL2772_CNTL_INTPROXPON_ENBL 0x2F - -#define TSL2772_ALS_GAIN_TRIM_MIN 250 -#define TSL2772_ALS_GAIN_TRIM_MAX 4000 - -/* Device family members */ -enum { - tsl2571, - tsl2671, - tmd2671, - tsl2771, - tmd2771, - tsl2572, - tsl2672, - tmd2672, - tsl2772, - tmd2772 -}; - -enum { - TSL2772_CHIP_UNKNOWN = 0, - TSL2772_CHIP_WORKING = 1, - TSL2772_CHIP_SUSPENDED = 2 -}; - -/* Per-device data */ -struct tsl2772_als_info { - u16 als_ch0; - u16 als_ch1; - u16 lux; -}; - -struct tsl2772_chip_info { - int chan_table_elements; - struct iio_chan_spec channel_with_events[4]; - struct iio_chan_spec channel_without_events[4]; - const struct iio_info *info; -}; - -struct tsl2772_chip { - kernel_ulong_t id; - struct mutex prox_mutex; - struct mutex als_mutex; - struct i2c_client *client; - u16 prox_data; - struct tsl2772_als_info als_cur_info; - struct tsl2772_settings settings; - struct tsl2772_platform_data *pdata; - int als_gain_time_scale; - int als_saturation; - int tsl2772_chip_status; - u8 tsl2772_config[TSL2772_MAX_CONFIG_REG]; - const struct tsl2772_chip_info *chip_info; - const struct iio_info *info; - s64 event_timestamp; - /* - * This structure is intentionally large to accommodate - * updates via sysfs. - * Sized to 9 = max 8 segments + 1 termination segment - */ - struct tsl2772_lux tsl2772_device_lux[TSL2772_MAX_LUX_TABLE_SIZE]; -}; - -/* - * Different devices require different coefficents, and these numbers were - * derived from the 'Lux Equation' section of the various device datasheets. - * All of these coefficients assume a Glass Attenuation (GA) factor of 1. - * The coefficients are multiplied by 1000 to avoid floating point operations. - * The two rows in each table correspond to the Lux1 and Lux2 equations from - * the datasheets. - */ -static const struct tsl2772_lux tsl2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { - { 53000, 106000 }, - { 31800, 53000 }, - { 0, 0 }, -}; - -static const struct tsl2772_lux tmd2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { - { 24000, 48000 }, - { 14400, 24000 }, - { 0, 0 }, -}; - -static const struct tsl2772_lux tsl2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { - { 60000, 112200 }, - { 37800, 60000 }, - { 0, 0 }, -}; - -static const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { - { 20000, 35000 }, - { 12600, 20000 }, - { 0, 0 }, -}; - -static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { - [tsl2571] = tsl2x71_lux_table, - [tsl2671] = tsl2x71_lux_table, - [tmd2671] = tmd2x71_lux_table, - [tsl2771] = tsl2x71_lux_table, - [tmd2771] = tmd2x71_lux_table, - [tsl2572] = tsl2x72_lux_table, - [tsl2672] = tsl2x72_lux_table, - [tmd2672] = tmd2x72_lux_table, - [tsl2772] = tsl2x72_lux_table, - [tmd2772] = tmd2x72_lux_table, -}; - -static const struct tsl2772_settings tsl2772_default_settings = { - .als_time = 255, /* 2.72 / 2.73 ms */ - .als_gain = 0, - .prox_time = 255, /* 2.72 / 2.73 ms */ - .prox_gain = 0, - .wait_time = 255, - .als_prox_config = 0, - .als_gain_trim = 1000, - .als_cal_target = 150, - .als_persistence = 1, - .als_interrupt_en = false, - .als_thresh_low = 200, - .als_thresh_high = 256, - .prox_persistence = 1, - .prox_interrupt_en = false, - .prox_thres_low = 0, - .prox_thres_high = 512, - .prox_max_samples_cal = 30, - .prox_pulse_count = 8, - .prox_diode = TSL2772_DIODE1, - .prox_power = TSL2772_100_mA -}; - -static const s16 tsl2772_als_gain[] = { - 1, - 8, - 16, - 120 -}; - -static const s16 tsl2772_prox_gain[] = { - 1, - 2, - 4, - 8 -}; - -static const int tsl2772_int_time_avail[][6] = { - [tsl2571] = { 0, 2720, 0, 2720, 0, 696000 }, - [tsl2671] = { 0, 2720, 0, 2720, 0, 696000 }, - [tmd2671] = { 0, 2720, 0, 2720, 0, 696000 }, - [tsl2771] = { 0, 2720, 0, 2720, 0, 696000 }, - [tmd2771] = { 0, 2720, 0, 2720, 0, 696000 }, - [tsl2572] = { 0, 2730, 0, 2730, 0, 699000 }, - [tsl2672] = { 0, 2730, 0, 2730, 0, 699000 }, - [tmd2672] = { 0, 2730, 0, 2730, 0, 699000 }, - [tsl2772] = { 0, 2730, 0, 2730, 0, 699000 }, - [tmd2772] = { 0, 2730, 0, 2730, 0, 699000 }, -}; - -static int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 }; - -static int tsl2772_prox_calibscale_avail[] = { 1, 2, 4, 8 }; - -/* Channel variations */ -enum { - ALS, - PRX, - ALSPRX, - PRX2, - ALSPRX2, -}; - -static const u8 device_channel_config[] = { - [tsl2571] = ALS, - [tsl2671] = PRX, - [tmd2671] = PRX, - [tsl2771] = ALSPRX, - [tmd2771] = ALSPRX, - [tsl2572] = ALS, - [tsl2672] = PRX2, - [tmd2672] = PRX2, - [tsl2772] = ALSPRX2, - [tmd2772] = ALSPRX2 -}; - -static int tsl2772_read_status(struct tsl2772_chip *chip) -{ - int ret; - - ret = i2c_smbus_read_byte_data(chip->client, - TSL2772_CMD_REG | TSL2772_STATUS); - if (ret < 0) - dev_err(&chip->client->dev, - "%s: failed to read STATUS register: %d\n", __func__, - ret); - - return ret; -} - -static int tsl2772_write_control_reg(struct tsl2772_chip *chip, u8 data) -{ - int ret; - - ret = i2c_smbus_write_byte_data(chip->client, - TSL2772_CMD_REG | TSL2772_CNTRL, data); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to write to control register %x: %d\n", - __func__, data, ret); - } - - return ret; -} - -static int tsl2772_read_autoinc_regs(struct tsl2772_chip *chip, int lower_reg, - int upper_reg) -{ - u8 buf[2]; - int ret; - - ret = i2c_smbus_write_byte(chip->client, - TSL2772_CMD_REG | TSL2772_CMD_AUTOINC_PROTO | - lower_reg); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to enable auto increment protocol: %d\n", - __func__, ret); - return ret; - } - - ret = i2c_smbus_read_byte_data(chip->client, - TSL2772_CMD_REG | lower_reg); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to read from register %x: %d\n", __func__, - lower_reg, ret); - return ret; - } - buf[0] = ret; - - ret = i2c_smbus_read_byte_data(chip->client, - TSL2772_CMD_REG | upper_reg); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to read from register %x: %d\n", __func__, - upper_reg, ret); - return ret; - } - buf[1] = ret; - - ret = i2c_smbus_write_byte(chip->client, - TSL2772_CMD_REG | TSL2772_CMD_REPEAT_PROTO | - lower_reg); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to enable repeated byte protocol: %d\n", - __func__, ret); - return ret; - } - - return le16_to_cpup((const __le16 *)&buf[0]); -} - -/** - * tsl2772_get_lux() - Reads and calculates current lux value. - * @indio_dev: pointer to IIO device - * - * The raw ch0 and ch1 values of the ambient light sensed in the last - * integration cycle are read from the device. The raw values are multiplied - * by a device-specific scale factor, and divided by the integration time and - * device gain. The code supports multiple lux equations through the lux table - * coefficients. A lux gain trim is applied to each lux equation, and then the - * maximum lux within the interval 0..65535 is selected. - */ -static int tsl2772_get_lux(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - struct tsl2772_lux *p; - int max_lux, ret; - bool overflow; - - mutex_lock(&chip->als_mutex); - - if (chip->tsl2772_chip_status != TSL2772_CHIP_WORKING) { - dev_err(&chip->client->dev, "%s: device is not enabled\n", - __func__); - ret = -EBUSY; - goto out_unlock; - } - - ret = tsl2772_read_status(chip); - if (ret < 0) - goto out_unlock; - - if (!(ret & TSL2772_STA_ADC_VALID)) { - dev_err(&chip->client->dev, - "%s: data not valid yet\n", __func__); - ret = chip->als_cur_info.lux; /* return LAST VALUE */ - goto out_unlock; - } - - ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN0LO, - TSL2772_ALS_CHAN0HI); - if (ret < 0) - goto out_unlock; - chip->als_cur_info.als_ch0 = ret; - - ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN1LO, - TSL2772_ALS_CHAN1HI); - if (ret < 0) - goto out_unlock; - chip->als_cur_info.als_ch1 = ret; - - if (chip->als_cur_info.als_ch0 >= chip->als_saturation) { - max_lux = TSL2772_LUX_CALC_OVER_FLOW; - goto update_struct_with_max_lux; - } - - if (!chip->als_cur_info.als_ch0) { - /* have no data, so return LAST VALUE */ - ret = chip->als_cur_info.lux; - goto out_unlock; - } - - max_lux = 0; - overflow = false; - for (p = (struct tsl2772_lux *)chip->tsl2772_device_lux; p->ch0 != 0; - p++) { - int lux; - - lux = ((chip->als_cur_info.als_ch0 * p->ch0) - - (chip->als_cur_info.als_ch1 * p->ch1)) / - chip->als_gain_time_scale; - - /* - * The als_gain_trim can have a value within the range 250..4000 - * and is a multiplier for the lux. A trim of 1000 makes no - * changes to the lux, less than 1000 scales it down, and - * greater than 1000 scales it up. - */ - lux = (lux * chip->settings.als_gain_trim) / 1000; - - if (lux > TSL2772_LUX_CALC_OVER_FLOW) { - overflow = true; - continue; - } - - max_lux = max(max_lux, lux); - } - - if (overflow && max_lux == 0) - max_lux = TSL2772_LUX_CALC_OVER_FLOW; - -update_struct_with_max_lux: - chip->als_cur_info.lux = max_lux; - ret = max_lux; - -out_unlock: - mutex_unlock(&chip->als_mutex); - - return ret; -} - -/** - * tsl2772_get_prox() - Reads proximity data registers and updates - * chip->prox_data. - * - * @indio_dev: pointer to IIO device - */ -static int tsl2772_get_prox(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int ret; - - mutex_lock(&chip->prox_mutex); - - ret = tsl2772_read_status(chip); - if (ret < 0) - goto prox_poll_err; - - switch (chip->id) { - case tsl2571: - case tsl2671: - case tmd2671: - case tsl2771: - case tmd2771: - if (!(ret & TSL2772_STA_ADC_VALID)) { - ret = -EINVAL; - goto prox_poll_err; - } - break; - case tsl2572: - case tsl2672: - case tmd2672: - case tsl2772: - case tmd2772: - if (!(ret & TSL2772_STA_PRX_VALID)) { - ret = -EINVAL; - goto prox_poll_err; - } - break; - } - - ret = tsl2772_read_autoinc_regs(chip, TSL2772_PRX_LO, TSL2772_PRX_HI); - if (ret < 0) - goto prox_poll_err; - chip->prox_data = ret; - -prox_poll_err: - mutex_unlock(&chip->prox_mutex); - - return ret; -} - -/** - * tsl2772_defaults() - Populates the device nominal operating parameters - * with those provided by a 'platform' data struct or - * with prefined defaults. - * - * @chip: pointer to device structure. - */ -static void tsl2772_defaults(struct tsl2772_chip *chip) -{ - /* If Operational settings defined elsewhere.. */ - if (chip->pdata && chip->pdata->platform_default_settings) - memcpy(&chip->settings, chip->pdata->platform_default_settings, - sizeof(tsl2772_default_settings)); - else - memcpy(&chip->settings, &tsl2772_default_settings, - sizeof(tsl2772_default_settings)); - - /* Load up the proper lux table. */ - if (chip->pdata && chip->pdata->platform_lux_table[0].ch0 != 0) - memcpy(chip->tsl2772_device_lux, - chip->pdata->platform_lux_table, - sizeof(chip->pdata->platform_lux_table)); - else - memcpy(chip->tsl2772_device_lux, - tsl2772_default_lux_table_group[chip->id], - TSL2772_DEFAULT_TABLE_BYTES); -} - -/** - * tsl2772_als_calibrate() - Obtain single reading and calculate - * the als_gain_trim. - * - * @indio_dev: pointer to IIO device - */ -static int tsl2772_als_calibrate(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int ret, lux_val; - - ret = i2c_smbus_read_byte_data(chip->client, - TSL2772_CMD_REG | TSL2772_CNTRL); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to read from the CNTRL register\n", - __func__); - return ret; - } - - if ((ret & (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) - != (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) { - dev_err(&chip->client->dev, - "%s: Device is not powered on and/or ADC is not enabled\n", - __func__); - return -EINVAL; - } else if ((ret & TSL2772_STA_ADC_VALID) != TSL2772_STA_ADC_VALID) { - dev_err(&chip->client->dev, - "%s: The two ADC channels have not completed an integration cycle\n", - __func__); - return -ENODATA; - } - - lux_val = tsl2772_get_lux(indio_dev); - if (lux_val < 0) { - dev_err(&chip->client->dev, - "%s: failed to get lux\n", __func__); - return lux_val; - } - - ret = (chip->settings.als_cal_target * chip->settings.als_gain_trim) / - lux_val; - if (ret < TSL2772_ALS_GAIN_TRIM_MIN || ret > TSL2772_ALS_GAIN_TRIM_MAX) - return -ERANGE; - - chip->settings.als_gain_trim = ret; - - return ret; -} - -static int tsl2772_chip_on(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int ret, i, als_count, als_time_us; - u8 *dev_reg, reg_val; - - /* Non calculated parameters */ - chip->tsl2772_config[TSL2772_ALS_TIME] = chip->settings.als_time; - chip->tsl2772_config[TSL2772_PRX_TIME] = chip->settings.prox_time; - chip->tsl2772_config[TSL2772_WAIT_TIME] = chip->settings.wait_time; - chip->tsl2772_config[TSL2772_ALS_PRX_CONFIG] = - chip->settings.als_prox_config; - - chip->tsl2772_config[TSL2772_ALS_MINTHRESHLO] = - (chip->settings.als_thresh_low) & 0xFF; - chip->tsl2772_config[TSL2772_ALS_MINTHRESHHI] = - (chip->settings.als_thresh_low >> 8) & 0xFF; - chip->tsl2772_config[TSL2772_ALS_MAXTHRESHLO] = - (chip->settings.als_thresh_high) & 0xFF; - chip->tsl2772_config[TSL2772_ALS_MAXTHRESHHI] = - (chip->settings.als_thresh_high >> 8) & 0xFF; - chip->tsl2772_config[TSL2772_PERSISTENCE] = - (chip->settings.prox_persistence & 0xFF) << 4 | - (chip->settings.als_persistence & 0xFF); - - chip->tsl2772_config[TSL2772_PRX_COUNT] = - chip->settings.prox_pulse_count; - chip->tsl2772_config[TSL2772_PRX_MINTHRESHLO] = - (chip->settings.prox_thres_low) & 0xFF; - chip->tsl2772_config[TSL2772_PRX_MINTHRESHHI] = - (chip->settings.prox_thres_low >> 8) & 0xFF; - chip->tsl2772_config[TSL2772_PRX_MAXTHRESHLO] = - (chip->settings.prox_thres_high) & 0xFF; - chip->tsl2772_config[TSL2772_PRX_MAXTHRESHHI] = - (chip->settings.prox_thres_high >> 8) & 0xFF; - - /* and make sure we're not already on */ - if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { - /* if forcing a register update - turn off, then on */ - dev_info(&chip->client->dev, "device is already enabled\n"); - return -EINVAL; - } - - /* Set the gain based on tsl2772_settings struct */ - chip->tsl2772_config[TSL2772_GAIN] = - (chip->settings.als_gain & 0xFF) | - ((chip->settings.prox_gain & 0xFF) << 2) | - (chip->settings.prox_diode << 4) | - (chip->settings.prox_power << 6); - - /* set chip time scaling and saturation */ - als_count = 256 - chip->settings.als_time; - als_time_us = als_count * tsl2772_int_time_avail[chip->id][3]; - chip->als_saturation = als_count * 768; /* 75% of full scale */ - chip->als_gain_time_scale = als_time_us * - tsl2772_als_gain[chip->settings.als_gain]; - - /* - * TSL2772 Specific power-on / adc enable sequence - * Power on the device 1st. - */ - ret = tsl2772_write_control_reg(chip, TSL2772_CNTL_PWR_ON); - if (ret < 0) - return ret; - - /* - * Use the following shadow copy for our delay before enabling ADC. - * Write all the registers. - */ - for (i = 0, dev_reg = chip->tsl2772_config; - i < TSL2772_MAX_CONFIG_REG; i++) { - int reg = TSL2772_CMD_REG + i; - - ret = i2c_smbus_write_byte_data(chip->client, reg, - *dev_reg++); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to write to register %x: %d\n", - __func__, reg, ret); - return ret; - } - } - - /* Power-on settling time */ - usleep_range(3000, 3500); - - reg_val = TSL2772_CNTL_PWR_ON | TSL2772_CNTL_ADC_ENBL | - TSL2772_CNTL_PROX_DET_ENBL; - if (chip->settings.als_interrupt_en) - reg_val |= TSL2772_CNTL_ALS_INT_ENBL; - if (chip->settings.prox_interrupt_en) - reg_val |= TSL2772_CNTL_PROX_INT_ENBL; - - ret = tsl2772_write_control_reg(chip, reg_val); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte(chip->client, - TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | - TSL2772_CMD_PROXALS_INT_CLR); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to clear interrupt status: %d\n", - __func__, ret); - return ret; - } - - chip->tsl2772_chip_status = TSL2772_CHIP_WORKING; - - return ret; -} - -static int tsl2772_chip_off(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - /* turn device off */ - chip->tsl2772_chip_status = TSL2772_CHIP_SUSPENDED; - return tsl2772_write_control_reg(chip, 0x00); -} - -/** - * tsl2772_invoke_change - power cycle the device to implement the user - * parameters - * @indio_dev: pointer to IIO device - * - * Obtain and lock both ALS and PROX resources, determine and save device state - * (On/Off), cycle device to implement updated parameter, put device back into - * proper state, and unlock resource. - */ -static int tsl2772_invoke_change(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int device_status = chip->tsl2772_chip_status; - int ret; - - mutex_lock(&chip->als_mutex); - mutex_lock(&chip->prox_mutex); - - if (device_status == TSL2772_CHIP_WORKING) { - ret = tsl2772_chip_off(indio_dev); - if (ret < 0) - goto unlock; - } - - ret = tsl2772_chip_on(indio_dev); - -unlock: - mutex_unlock(&chip->prox_mutex); - mutex_unlock(&chip->als_mutex); - - return ret; -} - -static int tsl2772_prox_cal(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int prox_history[MAX_SAMPLES_CAL + 1]; - int i, ret, mean, max, sample_sum; - - if (chip->settings.prox_max_samples_cal < 1 || - chip->settings.prox_max_samples_cal > MAX_SAMPLES_CAL) - return -EINVAL; - - for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { - usleep_range(15000, 17500); - ret = tsl2772_get_prox(indio_dev); - if (ret < 0) - return ret; - - prox_history[i] = chip->prox_data; - } - - sample_sum = 0; - max = INT_MIN; - for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { - sample_sum += prox_history[i]; - max = max(max, prox_history[i]); - } - mean = sample_sum / chip->settings.prox_max_samples_cal; - - chip->settings.prox_thres_high = (max << 1) - mean; - - return tsl2772_invoke_change(indio_dev); -} - -static int tsl2772_read_avail(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - const int **vals, int *type, int *length, - long mask) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_CALIBSCALE: - if (chan->type == IIO_INTENSITY) { - *length = ARRAY_SIZE(tsl2772_int_calibscale_avail); - *vals = tsl2772_int_calibscale_avail; - } else { - *length = ARRAY_SIZE(tsl2772_prox_calibscale_avail); - *vals = tsl2772_prox_calibscale_avail; - } - *type = IIO_VAL_INT; - return IIO_AVAIL_LIST; - case IIO_CHAN_INFO_INT_TIME: - *length = ARRAY_SIZE(tsl2772_int_time_avail[chip->id]); - *vals = tsl2772_int_time_avail[chip->id]; - *type = IIO_VAL_INT_PLUS_MICRO; - return IIO_AVAIL_RANGE; - } - - return -EINVAL; -} - -static ssize_t in_illuminance0_target_input_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); - - return snprintf(buf, PAGE_SIZE, "%d\n", chip->settings.als_cal_target); -} - -static ssize_t in_illuminance0_target_input_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct tsl2772_chip *chip = iio_priv(indio_dev); - u16 value; - int ret; - - if (kstrtou16(buf, 0, &value)) - return -EINVAL; - - chip->settings.als_cal_target = value; - ret = tsl2772_invoke_change(indio_dev); - if (ret < 0) - return ret; - - return len; -} - -static ssize_t in_illuminance0_calibrate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - bool value; - int ret; - - if (kstrtobool(buf, &value) || !value) - return -EINVAL; - - ret = tsl2772_als_calibrate(indio_dev); - if (ret < 0) - return ret; - - ret = tsl2772_invoke_change(indio_dev); - if (ret < 0) - return ret; - - return len; -} - -static ssize_t in_illuminance0_lux_table_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); - int i = 0; - int offset = 0; - - while (i < TSL2772_MAX_LUX_TABLE_SIZE) { - offset += snprintf(buf + offset, PAGE_SIZE, "%u,%u,", - chip->tsl2772_device_lux[i].ch0, - chip->tsl2772_device_lux[i].ch1); - if (chip->tsl2772_device_lux[i].ch0 == 0) { - /* - * We just printed the first "0" entry. - * Now get rid of the extra "," and break. - */ - offset--; - break; - } - i++; - } - - offset += snprintf(buf + offset, PAGE_SIZE, "\n"); - return offset; -} - -static ssize_t in_illuminance0_lux_table_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct tsl2772_chip *chip = iio_priv(indio_dev); - int value[ARRAY_SIZE(chip->tsl2772_device_lux) * 2 + 1]; - int n, ret; - - get_options(buf, ARRAY_SIZE(value), value); - - /* - * We now have an array of ints starting at value[1], and - * enumerated by value[0]. - * We expect each group of two ints to be one table entry, - * and the last table entry is all 0. - */ - n = value[0]; - if ((n % 2) || n < 4 || - n > ((ARRAY_SIZE(chip->tsl2772_device_lux) - 1) * 2)) - return -EINVAL; - - if ((value[(n - 1)] | value[n]) != 0) - return -EINVAL; - - if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { - ret = tsl2772_chip_off(indio_dev); - if (ret < 0) - return ret; - } - - /* Zero out the table */ - memset(chip->tsl2772_device_lux, 0, sizeof(chip->tsl2772_device_lux)); - memcpy(chip->tsl2772_device_lux, &value[1], (value[0] * 4)); - - ret = tsl2772_invoke_change(indio_dev); - if (ret < 0) - return ret; - - return len; -} - -static ssize_t in_proximity0_calibrate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - bool value; - int ret; - - if (kstrtobool(buf, &value) || !value) - return -EINVAL; - - ret = tsl2772_prox_cal(indio_dev); - if (ret < 0) - return ret; - - ret = tsl2772_invoke_change(indio_dev); - if (ret < 0) - return ret; - - return len; -} - -static int tsl2772_read_interrupt_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - if (chan->type == IIO_INTENSITY) - return chip->settings.als_interrupt_en; - else - return chip->settings.prox_interrupt_en; -} - -static int tsl2772_write_interrupt_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - int val) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - if (chan->type == IIO_INTENSITY) - chip->settings.als_interrupt_en = val ? true : false; - else - chip->settings.prox_interrupt_en = val ? true : false; - - return tsl2772_invoke_change(indio_dev); -} - -static int tsl2772_write_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int val, int val2) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int ret = -EINVAL, count, persistence; - u8 time; - - switch (info) { - case IIO_EV_INFO_VALUE: - if (chan->type == IIO_INTENSITY) { - switch (dir) { - case IIO_EV_DIR_RISING: - chip->settings.als_thresh_high = val; - ret = 0; - break; - case IIO_EV_DIR_FALLING: - chip->settings.als_thresh_low = val; - ret = 0; - break; - default: - break; - } - } else { - switch (dir) { - case IIO_EV_DIR_RISING: - chip->settings.prox_thres_high = val; - ret = 0; - break; - case IIO_EV_DIR_FALLING: - chip->settings.prox_thres_low = val; - ret = 0; - break; - default: - break; - } - } - break; - case IIO_EV_INFO_PERIOD: - if (chan->type == IIO_INTENSITY) - time = chip->settings.als_time; - else - time = chip->settings.prox_time; - - count = 256 - time; - persistence = ((val * 1000000) + val2) / - (count * tsl2772_int_time_avail[chip->id][3]); - - if (chan->type == IIO_INTENSITY) { - /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ - if (persistence > 3) - persistence = (persistence / 5) + 3; - - chip->settings.als_persistence = persistence; - } else { - chip->settings.prox_persistence = persistence; - } - - ret = 0; - break; - default: - break; - } - - if (ret < 0) - return ret; - - return tsl2772_invoke_change(indio_dev); -} - -static int tsl2772_read_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int *val, int *val2) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int filter_delay, persistence; - u8 time; - - switch (info) { - case IIO_EV_INFO_VALUE: - if (chan->type == IIO_INTENSITY) { - switch (dir) { - case IIO_EV_DIR_RISING: - *val = chip->settings.als_thresh_high; - return IIO_VAL_INT; - case IIO_EV_DIR_FALLING: - *val = chip->settings.als_thresh_low; - return IIO_VAL_INT; - default: - return -EINVAL; - } - } else { - switch (dir) { - case IIO_EV_DIR_RISING: - *val = chip->settings.prox_thres_high; - return IIO_VAL_INT; - case IIO_EV_DIR_FALLING: - *val = chip->settings.prox_thres_low; - return IIO_VAL_INT; - default: - return -EINVAL; - } - } - break; - case IIO_EV_INFO_PERIOD: - if (chan->type == IIO_INTENSITY) { - time = chip->settings.als_time; - persistence = chip->settings.als_persistence; - - /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ - if (persistence > 3) - persistence = (persistence - 3) * 5; - } else { - time = chip->settings.prox_time; - persistence = chip->settings.prox_persistence; - } - - filter_delay = persistence * (256 - time) * - tsl2772_int_time_avail[chip->id][3]; - - *val = filter_delay / 1000000; - *val2 = filter_delay % 1000000; - return IIO_VAL_INT_PLUS_MICRO; - default: - return -EINVAL; - } -} - -static int tsl2772_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long mask) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_PROCESSED: - switch (chan->type) { - case IIO_LIGHT: - tsl2772_get_lux(indio_dev); - *val = chip->als_cur_info.lux; - return IIO_VAL_INT; - default: - return -EINVAL; - } - case IIO_CHAN_INFO_RAW: - switch (chan->type) { - case IIO_INTENSITY: - tsl2772_get_lux(indio_dev); - if (chan->channel == 0) - *val = chip->als_cur_info.als_ch0; - else - *val = chip->als_cur_info.als_ch1; - return IIO_VAL_INT; - case IIO_PROXIMITY: - tsl2772_get_prox(indio_dev); - *val = chip->prox_data; - return IIO_VAL_INT; - default: - return -EINVAL; - } - break; - case IIO_CHAN_INFO_CALIBSCALE: - if (chan->type == IIO_LIGHT) - *val = tsl2772_als_gain[chip->settings.als_gain]; - else - *val = tsl2772_prox_gain[chip->settings.prox_gain]; - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBBIAS: - *val = chip->settings.als_gain_trim; - return IIO_VAL_INT; - case IIO_CHAN_INFO_INT_TIME: - *val = 0; - *val2 = (256 - chip->settings.als_time) * - tsl2772_int_time_avail[chip->id][3]; - return IIO_VAL_INT_PLUS_MICRO; - default: - return -EINVAL; - } -} - -static int tsl2772_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_CALIBSCALE: - if (chan->type == IIO_INTENSITY) { - switch (val) { - case 1: - chip->settings.als_gain = 0; - break; - case 8: - chip->settings.als_gain = 1; - break; - case 16: - chip->settings.als_gain = 2; - break; - case 120: - chip->settings.als_gain = 3; - break; - default: - return -EINVAL; - } - } else { - switch (val) { - case 1: - chip->settings.prox_gain = 0; - break; - case 2: - chip->settings.prox_gain = 1; - break; - case 4: - chip->settings.prox_gain = 2; - break; - case 8: - chip->settings.prox_gain = 3; - break; - default: - return -EINVAL; - } - } - break; - case IIO_CHAN_INFO_CALIBBIAS: - if (val < TSL2772_ALS_GAIN_TRIM_MIN || - val > TSL2772_ALS_GAIN_TRIM_MAX) - return -EINVAL; - - chip->settings.als_gain_trim = val; - break; - case IIO_CHAN_INFO_INT_TIME: - if (val != 0 || val2 < tsl2772_int_time_avail[chip->id][1] || - val2 > tsl2772_int_time_avail[chip->id][5]) - return -EINVAL; - - chip->settings.als_time = 256 - - (val2 / tsl2772_int_time_avail[chip->id][3]); - break; - default: - return -EINVAL; - } - - return tsl2772_invoke_change(indio_dev); -} - -static DEVICE_ATTR_RW(in_illuminance0_target_input); - -static DEVICE_ATTR_WO(in_illuminance0_calibrate); - -static DEVICE_ATTR_WO(in_proximity0_calibrate); - -static DEVICE_ATTR_RW(in_illuminance0_lux_table); - -/* Use the default register values to identify the Taos device */ -static int tsl2772_device_id_verif(int id, int target) -{ - switch (target) { - case tsl2571: - case tsl2671: - case tsl2771: - return (id & 0xf0) == TRITON_ID; - case tmd2671: - case tmd2771: - return (id & 0xf0) == HALIBUT_ID; - case tsl2572: - case tsl2672: - case tmd2672: - case tsl2772: - case tmd2772: - return (id & 0xf0) == SWORDFISH_ID; - } - - return -EINVAL; -} - -static irqreturn_t tsl2772_event_handler(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct tsl2772_chip *chip = iio_priv(indio_dev); - s64 timestamp = iio_get_time_ns(indio_dev); - int ret; - - ret = tsl2772_read_status(chip); - if (ret < 0) - return IRQ_HANDLED; - - /* What type of interrupt do we need to process */ - if (ret & TSL2772_STA_PRX_INTR) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_EITHER), - timestamp); - } - - if (ret & TSL2772_STA_ALS_INTR) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_LIGHT, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_EITHER), - timestamp); - } - - ret = i2c_smbus_write_byte(chip->client, - TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | - TSL2772_CMD_PROXALS_INT_CLR); - if (ret < 0) - dev_err(&chip->client->dev, - "%s: failed to clear interrupt status: %d\n", - __func__, ret); - - return IRQ_HANDLED; -} - -static struct attribute *tsl2772_ALS_device_attrs[] = { - &dev_attr_in_illuminance0_target_input.attr, - &dev_attr_in_illuminance0_calibrate.attr, - &dev_attr_in_illuminance0_lux_table.attr, - NULL -}; - -static struct attribute *tsl2772_PRX_device_attrs[] = { - &dev_attr_in_proximity0_calibrate.attr, - NULL -}; - -static struct attribute *tsl2772_ALSPRX_device_attrs[] = { - &dev_attr_in_illuminance0_target_input.attr, - &dev_attr_in_illuminance0_calibrate.attr, - &dev_attr_in_illuminance0_lux_table.attr, - NULL -}; - -static struct attribute *tsl2772_PRX2_device_attrs[] = { - &dev_attr_in_proximity0_calibrate.attr, - NULL -}; - -static struct attribute *tsl2772_ALSPRX2_device_attrs[] = { - &dev_attr_in_illuminance0_target_input.attr, - &dev_attr_in_illuminance0_calibrate.attr, - &dev_attr_in_illuminance0_lux_table.attr, - &dev_attr_in_proximity0_calibrate.attr, - NULL -}; - -static const struct attribute_group tsl2772_device_attr_group_tbl[] = { - [ALS] = { - .attrs = tsl2772_ALS_device_attrs, - }, - [PRX] = { - .attrs = tsl2772_PRX_device_attrs, - }, - [ALSPRX] = { - .attrs = tsl2772_ALSPRX_device_attrs, - }, - [PRX2] = { - .attrs = tsl2772_PRX2_device_attrs, - }, - [ALSPRX2] = { - .attrs = tsl2772_ALSPRX2_device_attrs, - }, -}; - -#define TSL2772_DEVICE_INFO(type)[type] = \ - { \ - .attrs = &tsl2772_device_attr_group_tbl[type], \ - .read_raw = &tsl2772_read_raw, \ - .read_avail = &tsl2772_read_avail, \ - .write_raw = &tsl2772_write_raw, \ - .read_event_value = &tsl2772_read_event_value, \ - .write_event_value = &tsl2772_write_event_value, \ - .read_event_config = &tsl2772_read_interrupt_config, \ - .write_event_config = &tsl2772_write_interrupt_config, \ - } - -static const struct iio_info tsl2772_device_info[] = { - TSL2772_DEVICE_INFO(ALS), - TSL2772_DEVICE_INFO(PRX), - TSL2772_DEVICE_INFO(ALSPRX), - TSL2772_DEVICE_INFO(PRX2), - TSL2772_DEVICE_INFO(ALSPRX2), -}; - -static const struct iio_event_spec tsl2772_events[] = { - { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_RISING, - .mask_separate = BIT(IIO_EV_INFO_VALUE), - }, { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_FALLING, - .mask_separate = BIT(IIO_EV_INFO_VALUE), - }, { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_EITHER, - .mask_separate = BIT(IIO_EV_INFO_PERIOD) | - BIT(IIO_EV_INFO_ENABLE), - }, -}; - -static const struct tsl2772_chip_info tsl2772_chip_info_tbl[] = { - [ALS] = { - .channel_with_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - }, - }, - .channel_without_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - }, - }, - .chan_table_elements = 3, - .info = &tsl2772_device_info[ALS], - }, - [PRX] = { - .channel_with_events = { - { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, - }, - .channel_without_events = { - { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, - }, - .chan_table_elements = 1, - .info = &tsl2772_device_info[PRX], - }, - [ALSPRX] = { - .channel_with_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, - }, - .channel_without_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, - }, - .chan_table_elements = 4, - .info = &tsl2772_device_info[ALSPRX], - }, - [PRX2] = { - .channel_with_events = { - { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_CALIBSCALE), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, - }, - .channel_without_events = { - { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_CALIBSCALE), - }, - }, - .chan_table_elements = 1, - .info = &tsl2772_device_info[PRX2], - }, - [ALSPRX2] = { - .channel_with_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_CALIBSCALE), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, - }, - .channel_without_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_CALIBSCALE), - }, - }, - .chan_table_elements = 4, - .info = &tsl2772_device_info[ALSPRX2], - }, -}; - -static int tsl2772_probe(struct i2c_client *clientp, - const struct i2c_device_id *id) -{ - struct iio_dev *indio_dev; - struct tsl2772_chip *chip; - int ret; - - indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip)); - if (!indio_dev) - return -ENOMEM; - - chip = iio_priv(indio_dev); - chip->client = clientp; - i2c_set_clientdata(clientp, indio_dev); - - ret = i2c_smbus_read_byte_data(chip->client, - TSL2772_CMD_REG | TSL2772_CHIPID); - if (ret < 0) - return ret; - - if (tsl2772_device_id_verif(ret, id->driver_data) <= 0) { - dev_info(&chip->client->dev, - "%s: i2c device found does not match expected id\n", - __func__); - return -EINVAL; - } - - ret = i2c_smbus_write_byte(clientp, TSL2772_CMD_REG | TSL2772_CNTRL); - if (ret < 0) { - dev_err(&clientp->dev, - "%s: Failed to write to CMD register: %d\n", - __func__, ret); - return ret; - } - - mutex_init(&chip->als_mutex); - mutex_init(&chip->prox_mutex); - - chip->tsl2772_chip_status = TSL2772_CHIP_UNKNOWN; - chip->pdata = dev_get_platdata(&clientp->dev); - chip->id = id->driver_data; - chip->chip_info = - &tsl2772_chip_info_tbl[device_channel_config[id->driver_data]]; - - indio_dev->info = chip->chip_info->info; - indio_dev->dev.parent = &clientp->dev; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->name = chip->client->name; - indio_dev->num_channels = chip->chip_info->chan_table_elements; - - if (clientp->irq) { - indio_dev->channels = chip->chip_info->channel_with_events; - - ret = devm_request_threaded_irq(&clientp->dev, clientp->irq, - NULL, - &tsl2772_event_handler, - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, - "TSL2772_event", - indio_dev); - if (ret) { - dev_err(&clientp->dev, - "%s: irq request failed\n", __func__); - return ret; - } - } else { - indio_dev->channels = chip->chip_info->channel_without_events; - } - - tsl2772_defaults(chip); - ret = tsl2772_chip_on(indio_dev); - if (ret < 0) - return ret; - - ret = iio_device_register(indio_dev); - if (ret) { - tsl2772_chip_off(indio_dev); - dev_err(&clientp->dev, - "%s: iio registration failed\n", __func__); - return ret; - } - - return 0; -} - -static int tsl2772_suspend(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - - return tsl2772_chip_off(indio_dev); -} - -static int tsl2772_resume(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - - return tsl2772_chip_on(indio_dev); -} - -static int tsl2772_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - tsl2772_chip_off(indio_dev); - - iio_device_unregister(indio_dev); - - return 0; -} - -static const struct i2c_device_id tsl2772_idtable[] = { - { "tsl2571", tsl2571 }, - { "tsl2671", tsl2671 }, - { "tmd2671", tmd2671 }, - { "tsl2771", tsl2771 }, - { "tmd2771", tmd2771 }, - { "tsl2572", tsl2572 }, - { "tsl2672", tsl2672 }, - { "tmd2672", tmd2672 }, - { "tsl2772", tsl2772 }, - { "tmd2772", tmd2772 }, - {} -}; - -MODULE_DEVICE_TABLE(i2c, tsl2772_idtable); - -static const struct of_device_id tsl2772_of_match[] = { - { .compatible = "amstaos,tsl2571" }, - { .compatible = "amstaos,tsl2671" }, - { .compatible = "amstaos,tmd2671" }, - { .compatible = "amstaos,tsl2771" }, - { .compatible = "amstaos,tmd2771" }, - { .compatible = "amstaos,tsl2572" }, - { .compatible = "amstaos,tsl2672" }, - { .compatible = "amstaos,tmd2672" }, - { .compatible = "amstaos,tsl2772" }, - { .compatible = "amstaos,tmd2772" }, - {} -}; -MODULE_DEVICE_TABLE(of, tsl2772_of_match); - -static const struct dev_pm_ops tsl2772_pm_ops = { - .suspend = tsl2772_suspend, - .resume = tsl2772_resume, -}; - -static struct i2c_driver tsl2772_driver = { - .driver = { - .name = "tsl2772", - .of_match_table = tsl2772_of_match, - .pm = &tsl2772_pm_ops, - }, - .id_table = tsl2772_idtable, - .probe = tsl2772_probe, - .remove = tsl2772_remove, -}; - -module_i2c_driver(tsl2772_driver); - -MODULE_AUTHOR("J. August Brenner "); -MODULE_AUTHOR("Brian Masney "); -MODULE_DESCRIPTION("TAOS tsl2772 ambient and proximity light sensor driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/light/tsl2772.h b/drivers/staging/iio/light/tsl2772.h deleted file mode 100644 index f8ade15a35e2..000000000000 --- a/drivers/staging/iio/light/tsl2772.h +++ /dev/null @@ -1,101 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Device driver for monitoring ambient light intensity (lux) - * and proximity (prox) within the TAOS TSL2772 family of devices. - * - * Copyright (c) 2012, TAOS Corporation. - * Copyright (c) 2017-2018 Brian Masney - */ - -#ifndef __TSL2772_H -#define __TSL2772_H - -struct tsl2772_lux { - unsigned int ch0; - unsigned int ch1; -}; - -/* Max number of segments allowable in LUX table */ -#define TSL2772_MAX_LUX_TABLE_SIZE 6 -/* The default LUX tables all have 3 elements. */ -#define TSL2772_DEF_LUX_TABLE_SZ 3 -#define TSL2772_DEFAULT_TABLE_BYTES (sizeof(struct tsl2772_lux) * \ - TSL2772_DEF_LUX_TABLE_SZ) - -/* Proximity diode to use */ -#define TSL2772_DIODE0 0x01 -#define TSL2772_DIODE1 0x02 -#define TSL2772_DIODE_BOTH 0x03 - -/* LED Power */ -#define TSL2772_100_mA 0x00 -#define TSL2772_50_mA 0x01 -#define TSL2772_25_mA 0x02 -#define TSL2772_13_mA 0x03 - -/** - * struct tsl2772_settings - Settings for the tsl2772 driver - * @als_time: Integration time of the ALS channel ADCs in 2.73 ms - * increments. Total integration time is - * (256 - als_time) * 2.73. - * @als_gain: Index into the tsl2772_als_gain array. - * @als_gain_trim: Default gain trim to account for aperture effects. - * @wait_time: Time between proximity and ALS cycles in 2.73 - * periods. - * @prox_time: Integration time of the proximity ADC in 2.73 ms - * increments. Total integration time is - * (256 - prx_time) * 2.73. - * @prox_gain: Index into the tsl2772_prx_gain array. - * @als_prox_config: The value of the ALS / Proximity configuration - * register. - * @als_cal_target: Known external ALS reading for calibration. - * @als_persistence: H/W Filters, Number of 'out of limits' ALS readings. - * @als_interrupt_en: Enable/Disable ALS interrupts - * @als_thresh_low: CH0 'low' count to trigger interrupt. - * @als_thresh_high: CH0 'high' count to trigger interrupt. - * @prox_persistence: H/W Filters, Number of 'out of limits' proximity - * readings. - * @prox_interrupt_en: Enable/Disable proximity interrupts. - * @prox_thres_low: Low threshold proximity detection. - * @prox_thres_high: High threshold proximity detection. - * @prox_pulse_count: Number if proximity emitter pulses. - * @prox_max_samples_cal: The number of samples that are taken when performing - * a proximity calibration. - * @prox_diode Which diode(s) to use for driving the external - * LED(s) for proximity sensing. - * @prox_power The amount of power to use for the external LED(s). - */ -struct tsl2772_settings { - int als_time; - int als_gain; - int als_gain_trim; - int wait_time; - int prox_time; - int prox_gain; - int als_prox_config; - int als_cal_target; - u8 als_persistence; - bool als_interrupt_en; - int als_thresh_low; - int als_thresh_high; - u8 prox_persistence; - bool prox_interrupt_en; - int prox_thres_low; - int prox_thres_high; - int prox_pulse_count; - int prox_max_samples_cal; - int prox_diode; - int prox_power; -}; - -/** - * struct tsl2772_platform_data - Platform callback, glass and defaults - * @platform_lux_table: Device specific glass coefficents - * @platform_default_settings: Device specific power on defaults - */ -struct tsl2772_platform_data { - struct tsl2772_lux platform_lux_table[TSL2772_MAX_LUX_TABLE_SIZE]; - struct tsl2772_settings *platform_default_settings; -}; - -#endif /* __TSL2772_H */ diff --git a/include/linux/platform_data/tsl2772.h b/include/linux/platform_data/tsl2772.h new file mode 100644 index 000000000000..f8ade15a35e2 --- /dev/null +++ b/include/linux/platform_data/tsl2772.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Device driver for monitoring ambient light intensity (lux) + * and proximity (prox) within the TAOS TSL2772 family of devices. + * + * Copyright (c) 2012, TAOS Corporation. + * Copyright (c) 2017-2018 Brian Masney + */ + +#ifndef __TSL2772_H +#define __TSL2772_H + +struct tsl2772_lux { + unsigned int ch0; + unsigned int ch1; +}; + +/* Max number of segments allowable in LUX table */ +#define TSL2772_MAX_LUX_TABLE_SIZE 6 +/* The default LUX tables all have 3 elements. */ +#define TSL2772_DEF_LUX_TABLE_SZ 3 +#define TSL2772_DEFAULT_TABLE_BYTES (sizeof(struct tsl2772_lux) * \ + TSL2772_DEF_LUX_TABLE_SZ) + +/* Proximity diode to use */ +#define TSL2772_DIODE0 0x01 +#define TSL2772_DIODE1 0x02 +#define TSL2772_DIODE_BOTH 0x03 + +/* LED Power */ +#define TSL2772_100_mA 0x00 +#define TSL2772_50_mA 0x01 +#define TSL2772_25_mA 0x02 +#define TSL2772_13_mA 0x03 + +/** + * struct tsl2772_settings - Settings for the tsl2772 driver + * @als_time: Integration time of the ALS channel ADCs in 2.73 ms + * increments. Total integration time is + * (256 - als_time) * 2.73. + * @als_gain: Index into the tsl2772_als_gain array. + * @als_gain_trim: Default gain trim to account for aperture effects. + * @wait_time: Time between proximity and ALS cycles in 2.73 + * periods. + * @prox_time: Integration time of the proximity ADC in 2.73 ms + * increments. Total integration time is + * (256 - prx_time) * 2.73. + * @prox_gain: Index into the tsl2772_prx_gain array. + * @als_prox_config: The value of the ALS / Proximity configuration + * register. + * @als_cal_target: Known external ALS reading for calibration. + * @als_persistence: H/W Filters, Number of 'out of limits' ALS readings. + * @als_interrupt_en: Enable/Disable ALS interrupts + * @als_thresh_low: CH0 'low' count to trigger interrupt. + * @als_thresh_high: CH0 'high' count to trigger interrupt. + * @prox_persistence: H/W Filters, Number of 'out of limits' proximity + * readings. + * @prox_interrupt_en: Enable/Disable proximity interrupts. + * @prox_thres_low: Low threshold proximity detection. + * @prox_thres_high: High threshold proximity detection. + * @prox_pulse_count: Number if proximity emitter pulses. + * @prox_max_samples_cal: The number of samples that are taken when performing + * a proximity calibration. + * @prox_diode Which diode(s) to use for driving the external + * LED(s) for proximity sensing. + * @prox_power The amount of power to use for the external LED(s). + */ +struct tsl2772_settings { + int als_time; + int als_gain; + int als_gain_trim; + int wait_time; + int prox_time; + int prox_gain; + int als_prox_config; + int als_cal_target; + u8 als_persistence; + bool als_interrupt_en; + int als_thresh_low; + int als_thresh_high; + u8 prox_persistence; + bool prox_interrupt_en; + int prox_thres_low; + int prox_thres_high; + int prox_pulse_count; + int prox_max_samples_cal; + int prox_diode; + int prox_power; +}; + +/** + * struct tsl2772_platform_data - Platform callback, glass and defaults + * @platform_lux_table: Device specific glass coefficents + * @platform_default_settings: Device specific power on defaults + */ +struct tsl2772_platform_data { + struct tsl2772_lux platform_lux_table[TSL2772_MAX_LUX_TABLE_SIZE]; + struct tsl2772_settings *platform_default_settings; +}; + +#endif /* __TSL2772_H */ -- cgit v1.2.3 From 98004a78bb6cf18c260cecb49cb01e36cf6a72be Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Tue, 27 Mar 2018 10:02:01 +0000 Subject: platform_data/mlxreg: Document fixes for hotplug device Remove redunadant description of label in struct mlxreg_hotplug_device. Change location of access_mode in struct mlxreg_hotplug_device. Signed-off-by: Vadim Pasternak Signed-off-by: Darren Hart (VMware) --- include/linux/platform_data/mlxreg.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h index 2744cff1b297..19f5cb618c55 100644 --- a/include/linux/platform_data/mlxreg.h +++ b/include/linux/platform_data/mlxreg.h @@ -58,11 +58,10 @@ struct mlxreg_hotplug_device { * struct mlxreg_core_data - attributes control data: * * @label: attribute label; - * @label: attribute register offset; * @reg: attribute register; * @mask: attribute access mask; - * @mode: access mode; * @bit: attribute effective bit; + * @mode: access mode; * @np - pointer to node platform associated with attribute; * @hpdev - hotplug device data; * @health_cntr: dynamic device health indication counter; -- cgit v1.2.3 From e7deb3c7741eaa558458696e55f57141886fcc5c Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 14 May 2018 15:47:30 +0200 Subject: drm: shmobile: remove unused MERAM support Since commit a521422ea4ae ("ARM: shmobile: mackerel: Remove Legacy C board code") MERAM functionality is unused. Remove it. Reviewed-by: Simon Horman Cc: Geert Uytterhoeven Acked-by: Laurent Pinchart Acked-by: Daniel Vetter Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/gpu/drm/shmobile/Kconfig | 1 - drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 42 ------------------------------ drivers/gpu/drm/shmobile/shmob_drm_crtc.h | 1 - drivers/gpu/drm/shmobile/shmob_drm_drv.h | 2 -- drivers/gpu/drm/shmobile/shmob_drm_kms.c | 11 -------- drivers/gpu/drm/shmobile/shmob_drm_kms.h | 1 - drivers/gpu/drm/shmobile/shmob_drm_plane.c | 2 -- include/linux/platform_data/shmob_drm.h | 4 --- 8 files changed, 64 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/gpu/drm/shmobile/Kconfig b/drivers/gpu/drm/shmobile/Kconfig index c987c826daa3..0426d66660d1 100644 --- a/drivers/gpu/drm/shmobile/Kconfig +++ b/drivers/gpu/drm/shmobile/Kconfig @@ -2,7 +2,6 @@ config DRM_SHMOBILE tristate "DRM Support for SH Mobile" depends on DRM && ARM depends on ARCH_SHMOBILE || COMPILE_TEST - depends on FB_SH_MOBILE_MERAM || !FB_SH_MOBILE_MERAM select BACKLIGHT_CLASS_DEVICE select BACKLIGHT_LCD_SUPPORT select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index e7738939a86d..40df8887fc17 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -21,8 +21,6 @@ #include #include -#include