Commit ab782659 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mfd-3.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6

Pull MFS updates from Samuel Ortiz:
 "This is the MFD pull request for the 3.9 merge window.

  No new drivers this time, but a bunch of fairly big cleanups:

   - Roger Quadros worked on a OMAP USBHS and TLL platform data
     consolidation, OMAP5 support and clock management code cleanup.

   - The first step of a major sync for the ab8500 driver from Lee
     Jones.  In particular, the debugfs and the sysct interfaces got
     extended and improved.

   - Peter Ujfalusi sent a nice patchset for cleaning and fixing the
     twl-core driver, with a much needed module id lookup code
     improvement.

   - The regular wm5102 and arizona cleanups and fixes from Mark Brown.

   - Laxman Dewangan extended the palmas APIs in order to implement the
     palmas GPIO and rt drivers.

   - Laxman also added DT support for the tps65090 driver.

   - The Intel SCH and ICH drivers got a couple fixes from Aaron Sierra
     and Darren Hart.

   - Linus Walleij patchset for the ab8500 driver allowed ab8500 and
     ab9540 based devices to switch to the new abx500 pin-ctrl driver.

   - The max8925 now has device tree and irqdomain support thanks to
     Qing Xu.

   - The recently added rtsx driver got a few cleanups and fixes for a
     better card detection code path and now also supports the RTS5227
     chipset, thanks to Wei Wang and Roger Tseng."

* tag 'mfd-3.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (109 commits)
  mfd: lpc_ich: Use devres API to allocate private data
  mfd: lpc_ich: Add Device IDs for Intel Wellsburg PCH
  mfd: lpc_sch: Accomodate partial population of the MFD devices
  mfd: da9052-i2c: Staticize da9052_i2c_fix()
  mfd: syscon: Fix sparse warning
  mfd: twl-core: Fix kernel panic on boot
  mfd: rtsx: Fix issue that booting OS with SD card inserted
  mfd: ab8500: Fix compile error
  mfd: Add missing GENERIC_HARDIRQS dependecies
  Documentation: Add docs for max8925 dt
  mfd: max8925: Add dts
  mfd: max8925: Support dt for backlight
  mfd: max8925: Fix onkey driver irq base
  mfd: max8925: Fix mfd device register failure
  mfd: max8925: Add irqdomain for dt
  mfd: vexpress: Allow vexpress-sysreg to self-initialise
  mfd: rtsx: Support RTS5227
  mfd: rtsx: Implement driving adjustment to device-dependent callbacks
  mfd: vexpress: Add pseudo-GPIO based LEDs
  mfd: ab8500: Rename ab8500 to abx500 for hwmon driver
  ...
parents 21fbd580 ff7109fa
* Maxim max8925 Power Management IC
Required parent device properties:
- compatible : "maxim,max8925"
- reg : the I2C slave address for the max8925 chip
- interrupts : IRQ line for the max8925 chip
- interrupt-controller: describes the max8925 as an interrupt
controller (has its own domain)
- #interrupt-cells : should be 1.
- The cell is the max8925 local IRQ number
Optional parent device properties:
- maxim,tsc-irq: there are 2 IRQ lines for max8925, one is indicated in
interrupts property, the other is indicated here.
max8925 consists of a large and varied group of sub-devices:
Device Supply Names Description
------ ------------ -----------
max8925-onkey : : On key
max8925-rtc : : RTC
max8925-regulator : : Regulators
max8925-backlight : : Backlight
max8925-touch : : Touchscreen
max8925-power : : Charger
Example:
pmic: max8925@3c {
compatible = "maxim,max8925";
reg = <0x3c>;
interrupts = <1>;
interrupt-parent = <&intcmux4>;
interrupt-controller;
#interrupt-cells = <1>;
maxim,tsc-irq = <0>;
regulators {
SDV1 {
regulator-min-microvolt = <637500>;
regulator-max-microvolt = <1425000>;
regulator-boot-on;
regulator-always-on;
};
LDO1 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
};
backlight {
maxim,max8925-dual-string = <0>;
};
charger {
batt-detect = <0>;
topoff-threshold = <1>;
fast-charge = <7>;
no-temp-support = <0>;
no-insert-detect = <0>;
};
};
max8925-battery bindings
~~~~~~~~~~~~~~~~
Optional properties :
- batt-detect: whether support battery detect
- topoff-threshold: set charging current in topoff mode
- fast-charge: set charging current in fast mode
- no-temp-support: whether support temperature protection detect
- no-insert-detect: whether support insert detect
Example:
charger {
batt-detect = <0>;
topoff-threshold = <1>;
fast-charge = <7>;
no-temp-support = <0>;
no-insert-detect = <0>;
};
TPS65090 regulators
Required properties:
- compatible: "ti,tps65090"
- reg: I2C slave address
- interrupts: the interrupt outputs of the controller
- regulators: A node that houses a sub-node for each regulator within the
device. Each sub-node is identified using the node's name, with valid
values listed below. The content of each sub-node is defined by the
standard binding for regulators; see regulator.txt.
dcdc[1-3], fet[1-7] and ldo[1-2] respectively.
- vsys[1-3]-supply: The input supply for DCDC[1-3] respectively.
- infet[1-7]-supply: The input supply for FET[1-7] respectively.
- vsys-l[1-2]-supply: The input supply for LDO[1-2] respectively.
Optional properties:
- ti,enable-ext-control: This is applicable for DCDC1, DCDC2 and DCDC3.
If DCDCs are externally controlled then this property should be there.
- "dcdc-ext-control-gpios: This is applicable for DCDC1, DCDC2 and DCDC3.
If DCDCs are externally controlled and if it is from GPIO then GPIO
number should be provided. If it is externally controlled and no GPIO
entry then driver will just configure this rails as external control
and will not provide any enable/disable APIs.
Each regulator is defined using the standard binding for regulators.
Example:
tps65090@48 {
compatible = "ti,tps65090";
reg = <0x48>;
interrupts = <0 88 0x4>;
vsys1-supply = <&some_reg>;
vsys2-supply = <&some_reg>;
vsys3-supply = <&some_reg>;
infet1-supply = <&some_reg>;
infet2-supply = <&some_reg>;
infet3-supply = <&some_reg>;
infet4-supply = <&some_reg>;
infet5-supply = <&some_reg>;
infet6-supply = <&some_reg>;
infet7-supply = <&some_reg>;
vsys_l1-supply = <&some_reg>;
vsys_l2-supply = <&some_reg>;
regulators {
dcdc1 {
regulator-name = "dcdc1";
regulator-boot-on;
regulator-always-on;
ti,enable-ext-control;
dcdc-ext-control-gpios = <&gpio 10 0>;
};
dcdc2 {
regulator-name = "dcdc2";
regulator-boot-on;
regulator-always-on;
};
dcdc3 {
regulator-name = "dcdc3";
regulator-boot-on;
regulator-always-on;
};
fet1 {
regulator-name = "fet1";
regulator-boot-on;
regulator-always-on;
};
fet2 {
regulator-name = "fet2";
regulator-boot-on;
regulator-always-on;
};
fet3 {
regulator-name = "fet3";
regulator-boot-on;
regulator-always-on;
};
fet4 {
regulator-name = "fet4";
regulator-boot-on;
regulator-always-on;
};
fet5 {
regulator-name = "fet5";
regulator-boot-on;
regulator-always-on;
};
fet6 {
regulator-name = "fet6";
regulator-boot-on;
regulator-always-on;
};
fet7 {
regulator-name = "fet7";
regulator-boot-on;
regulator-always-on;
};
ldo1 {
regulator-name = "ldo1";
regulator-boot-on;
regulator-always-on;
};
ldo2 {
regulator-name = "ldo2";
regulator-boot-on;
regulator-always-on;
};
};
};
88pm860x-backlight bindings
Optional properties:
- maxim,max8925-dual-string: whether support dual string
Example:
backlights {
maxim,max8925-dual-string = <0>;
};
......@@ -29,6 +29,164 @@ uart3: uart@d4018000 {
};
twsi1: i2c@d4011000 {
status = "okay";
pmic: max8925@3c {
compatible = "maxium,max8925";
reg = <0x3c>;
interrupts = <1>;
interrupt-parent = <&intcmux4>;
interrupt-controller;
#interrupt-cells = <1>;
maxim,tsc-irq = <0>;
regulators {
SDV1 {
regulator-min-microvolt = <637500>;
regulator-max-microvolt = <1425000>;
regulator-boot-on;
regulator-always-on;
};
SDV2 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2225000>;
regulator-boot-on;
regulator-always-on;
};
SDV3 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO1 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO2 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO3 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO4 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO5 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO6 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO7 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO8 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO9 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO10 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
};
LDO11 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO12 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO13 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO14 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO15 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO16 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO17 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO18 {
regulator-min-microvolt = <650000>;
regulator-max-microvolt = <2250000>;
regulator-boot-on;
regulator-always-on;
};
LDO19 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
LDO20 {
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <3900000>;
regulator-boot-on;
regulator-always-on;
};
};
backlight {
maxim,max8925-dual-string = <0>;
};
charger {
batt-detect = <0>;
topoff-threshold = <1>;
fast-charge = <7>;
no-temp-support = <0>;
no-insert-detect = <0>;
};
};
};
rtc: rtc@d4010000 {
status = "okay";
......
......@@ -46,7 +46,7 @@ intc: interrupt-controller@d4282000 {
mrvl,intc-nr-irqs = <64>;
};
intcmux4@d4282150 {
intcmux4: interrupt-controller@d4282150 {
compatible = "mrvl,mmp2-mux-intc";
interrupts = <4>;
interrupt-controller;
......@@ -201,6 +201,8 @@ twsi1: i2c@d4011000 {
compatible = "mrvl,mmp-twsi";
reg = <0xd4011000 0x1000>;
interrupts = <7>;
#address-cells = <1>;
#size-cells = <0>;
mrvl,i2c-fast-mode;
status = "disabled";
};
......
......@@ -49,13 +49,13 @@ static void zoom_panel_disable_lcd(struct omap_dss_device *dssdev)
{
}
/*
* PWMA/B register offsets (TWL4030_MODULE_PWMA)
*/
/* Register offsets in TWL4030_MODULE_INTBR */
#define TWL_INTBR_PMBR1 0xD
#define TWL_INTBR_GPBR1 0xC
#define TWL_LED_PWMON 0x0
#define TWL_LED_PWMOFF 0x1
/* Register offsets in TWL_MODULE_PWM */
#define TWL_LED_PWMON 0x3
#define TWL_LED_PWMOFF 0x4
static int zoom_set_bl_intensity(struct omap_dss_device *dssdev, int level)
{
......@@ -93,8 +93,8 @@ static int zoom_set_bl_intensity(struct omap_dss_device *dssdev, int level)
}
c = ((50 * (100 - level)) / 100) + 1;
twl_i2c_write_u8(TWL4030_MODULE_PWM1, 0x7F, TWL_LED_PWMOFF);
twl_i2c_write_u8(TWL4030_MODULE_PWM1, c, TWL_LED_PWMON);
twl_i2c_write_u8(TWL_MODULE_PWM, 0x7F, TWL_LED_PWMOFF);
twl_i2c_write_u8(TWL_MODULE_PWM, c, TWL_LED_PWMON);
#else
pr_warn("Backlight not enabled\n");
#endif
......
......@@ -657,6 +657,13 @@ config GPIO_JANZ_TTL
This driver provides support for driving the pins in output
mode only. Input mode is not supported.
config GPIO_PALMAS
bool "TI PALMAS series PMICs GPIO"
depends on MFD_PALMAS
help
Select this option to enable GPIO driver for the TI PALMAS
series chip family.
config GPIO_TPS6586X
bool "TPS6586X GPIO"
depends on MFD_TPS6586X
......
......@@ -68,6 +68,7 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o
obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
......
/*
* TI Palma series PMIC's GPIO driver.
*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*
* Author: Laxman Dewangan <ldewangan@nvidia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mfd/palmas.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
struct palmas_gpio {
struct gpio_chip gpio_chip;
struct palmas *palmas;
};
static inline struct palmas_gpio *to_palmas_gpio(struct gpio_chip *chip)
{
return container_of(chip, struct palmas_gpio, gpio_chip);
}
static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset)
{
struct palmas_gpio *pg = to_palmas_gpio(gc);
struct palmas *palmas = pg->palmas;
unsigned int val;
int ret;
ret = palmas_read(palmas, PALMAS_GPIO_BASE, PALMAS_GPIO_DATA_IN, &val);
if (ret < 0) {
dev_err(gc->dev, "GPIO_DATA_IN read failed, err = %d\n", ret);
return ret;
}
return !!(val & BIT(offset));
}
static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset,
int value)
{
struct palmas_gpio *pg = to_palmas_gpio(gc);
struct palmas *palmas = pg->palmas;
int ret;
if (value)
ret = palmas_write(palmas, PALMAS_GPIO_BASE,
PALMAS_GPIO_SET_DATA_OUT, BIT(offset));
else
ret = palmas_write(palmas, PALMAS_GPIO_BASE,
PALMAS_GPIO_CLEAR_DATA_OUT, BIT(offset));
if (ret < 0)
dev_err(gc->dev, "%s write failed, err = %d\n",
(value) ? "GPIO_SET_DATA_OUT" : "GPIO_CLEAR_DATA_OUT",
ret);
}
static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset,
int value)
{
struct palmas_gpio *pg = to_palmas_gpio(gc);
struct palmas *palmas = pg->palmas;
int ret;
/* Set the initial value */
palmas_gpio_set(gc, offset, value);
ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE,
PALMAS_GPIO_DATA_DIR, BIT(offset), BIT(offset));
if (ret < 0)
dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret);
return ret;
}
static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset)
{
struct palmas_gpio *pg = to_palmas_gpio(gc);
struct palmas *palmas = pg->palmas;
int ret;
ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE,
PALMAS_GPIO_DATA_DIR, BIT(offset), 0);
if (ret < 0)
dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret);
return ret;
}
static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct palmas_gpio *pg = to_palmas_gpio(gc);
struct palmas *palmas = pg->palmas;
return palmas_irq_get_virq(palmas, PALMAS_GPIO_0_IRQ + offset);
}
static int palmas_gpio_probe(struct platform_device *pdev)
{
struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
struct palmas_platform_data *palmas_pdata;
struct palmas_gpio *palmas_gpio;
int ret;
palmas_gpio = devm_kzalloc(&pdev->dev,
sizeof(*palmas_gpio), GFP_KERNEL);
if (!palmas_gpio) {
dev_err(&pdev->dev, "Could not allocate palmas_gpio\n");
return -ENOMEM;
}
palmas_gpio->palmas = palmas;
palmas_gpio->gpio_chip.owner = THIS_MODULE;
palmas_gpio->gpio_chip.label = dev_name(&pdev->dev);
palmas_gpio->gpio_chip.ngpio = 8;
palmas_gpio->gpio_chip.can_sleep = 1;
palmas_gpio->gpio_chip.direction_input = palmas_gpio_input;
palmas_gpio->gpio_chip.direction_output = palmas_gpio_output;
palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq;
palmas_gpio->gpio_chip.set = palmas_gpio_set;
palmas_gpio->gpio_chip.get = palmas_gpio_get;
palmas_gpio->gpio_chip.dev = &pdev->dev;
#ifdef CONFIG_OF_GPIO
palmas_gpio->gpio_chip.of_node = palmas->dev->of_node;
#endif
palmas_pdata = dev_get_platdata(palmas->dev);
if (palmas_pdata && palmas_pdata->gpio_base)
palmas_gpio->gpio_chip.base = palmas_pdata->gpio_base;
else
palmas_gpio->gpio_chip.base = -1;
ret = gpiochip_add(&palmas_gpio->gpio_chip);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
return ret;
}
platform_set_drvdata(pdev, palmas_gpio);
return ret;
}
static int palmas_gpio_remove(struct platform_device *pdev)
{
struct palmas_gpio *palmas_gpio = platform_get_drvdata(pdev);
return gpiochip_remove(&palmas_gpio->gpio_chip);
}
static struct platform_driver palmas_gpio_driver = {
.driver.name = "palmas-gpio",
.driver.owner = THIS_MODULE,
.probe = palmas_gpio_probe,
.remove = palmas_gpio_remove,
};
static int __init palmas_gpio_init(void)
{
return platform_driver_register(&palmas_gpio_driver);
}
subsys_initcall(palmas_gpio_init);
static void __exit palmas_gpio_exit(void)
{
platform_driver_unregister(&palmas_gpio_driver);
}
module_exit(palmas_gpio_exit);
MODULE_ALIAS("platform:palmas-gpio");
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_DESCRIPTION("GPIO driver for TI Palmas series PMICs");
MODULE_LICENSE("GPL v2");
......@@ -100,9 +100,6 @@ static int max8925_onkey_probe(struct platform_device *pdev)
input->dev.parent = &pdev->dev;
input_set_capability(input, EV_KEY, KEY_POWER);
irq[0] += chip->irq_base;
irq[1] += chip->irq_base;
error = request_threaded_irq(irq[0], NULL, max8925_onkey_handler,
IRQF_ONESHOT, "onkey-down", info);
if (error < 0) {
......
......@@ -426,6 +426,9 @@ static void rtsx_pci_ms_request(struct memstick_host *msh)
dev_dbg(ms_dev(host), "--> %s\n", __func__);
if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD))
return;
schedule_work(&host->handle_req);
}
......@@ -441,6 +444,10 @@ static int rtsx_pci_ms_set_param(struct memstick_host *msh,
dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
__func__, param, value);
err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD);
if (err)
return err;
switch (param) {
case MEMSTICK_POWER:
if (value == MEMSTICK_POWER_ON)
......
......@@ -531,7 +531,7 @@ static int pm800_probe(struct i2c_client *client,
ret = device_800_init(chip, pdata);
if (ret) {
dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
goto err_800_init;
goto err_subchip_alloc;
}
ret = pm800_pages_init(chip);
......@@ -546,10 +546,8 @@ static int pm800_probe(struct i2c_client *client,
err_page_init:
mfd_remove_devices(chip->dev);
device_irq_exit_800(chip);
err_800_init:
devm_kfree(&client->dev, subchip);
err_subchip_alloc:
pm80x_deinit(client);
pm80x_deinit();
out_init:
return ret;
}
......@@ -562,9 +560,7 @@ static int pm800_remove(struct i2c_client *client)
device_irq_exit_800(chip);
pm800_pages_exit(chip);
devm_kfree(&client->dev, chip->subchip);
pm80x_deinit(client);
pm80x_deinit();
return 0;
}
......
......@@ -257,7 +257,7 @@ static int pm805_probe(struct i2c_client *client,
pdata->plat_config(chip, pdata);
err_805_init:
pm80x_deinit(client);
pm80x_deinit();
out_init:
return ret;
}
......@@ -269,7 +269,7 @@ static int pm805_remove(struct i2c_client *client)
mfd_remove_devices(chip->dev);
device_irq_exit_805(chip);
pm80x_deinit(client);
pm80x_deinit();
return 0;
}
......
......@@ -48,14 +48,12 @@ int pm80x_init(struct i2c_client *client,
ret = PTR_ERR(map);
dev_err(&client->dev, "Failed to allocate register map: %d\n",
ret);
goto err_regmap_init;
return ret;
}
chip->id = id->driver_data;
if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) {
ret = -EINVAL;
goto err_chip_id;
}
if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805)
return -EINVAL;
chip->client = client;
chip->regmap = map;
......@@ -82,19 +80,11 @@ int pm80x_init(struct i2c_client *client,
}
return 0;
err_chip_id:
regmap_exit(map);
err_regmap_init:
devm_kfree(&client->dev, chip);
return ret;
}
EXPORT_SYMBOL_GPL(pm80x_init);
int pm80x_deinit(struct i2c_client *client)
int pm80x_deinit(void)
{
struct pm80x_chip *chip = i2c_get_clientdata(client);
/*
* workaround: clear the dependency between pm800 and pm805.
* would remove it after HW chip fixes the issue.
......@@ -103,10 +93,6 @@ int pm80x_deinit(struct i2c_client *client)
g_pm80x_chip->companion = NULL;
else
g_pm80x_chip = NULL;
regmap_exit(chip->regmap);
devm_kfree(&client->dev, chip);
return 0;
}
EXPORT_SYMBOL_GPL(pm80x_deinit);
......
......@@ -65,7 +65,7 @@ config MFD_SM501_GPIO
config MFD_RTSX_PCI
tristate "Support for Realtek PCI-E card reader"
depends on PCI
depends on PCI && GENERIC_HARDIRQS
select MFD_CORE
help
This supports for Realtek PCI-Express card reader including rts5209,
......@@ -95,7 +95,7 @@ config MFD_DM355EVM_MSP
config MFD_TI_SSP
tristate "TI Sequencer Serial Port support"
depends on ARCH_DAVINCI_TNETV107X
depends on ARCH_DAVINCI_TNETV107X && GENERIC_HARDIRQS
select MFD_CORE
---help---
Say Y here if you want support for the Sequencer Serial Port
......@@ -109,6 +109,7 @@ config MFD_TI_AM335X_TSCADC
select MFD_CORE
select REGMAP
select REGMAP_MMIO
depends on GENERIC_HARDIRQS
help
If you say yes here you get support for Texas Instruments series
of Touch Screen /ADC chips.
......@@ -126,6 +127,7 @@ config HTC_EGPIO
config HTC_PASIC3
tristate "HTC PASIC3 LED/DS1WM chip support"
select MFD_CORE
depends on GENERIC_HARDIRQS
help
This core driver provides register access for the LED/DS1WM
chips labeled "AIC2" and "AIC3", found on HTC Blueangel and
......@@ -157,6 +159,7 @@ config MFD_LM3533
depends on I2C
select MFD_CORE
select REGMAP_I2C
depends on GENERIC_HARDIRQS
help
Say yes here to enable support for National Semiconductor / TI
LM3533 Lighting Power chips.
......@@ -171,6 +174,7 @@ config TPS6105X
select REGULATOR
select MFD_CORE
select REGULATOR_FIXED_VOLTAGE
depends on GENERIC_HARDIRQS
help
This option enables a driver for the TP61050/TPS61052
high-power "white LED driver". This boost converter is
......@@ -193,7 +197,7 @@ config TPS65010
config TPS6507X
tristate "TPS6507x Power Management / Touch Screen chips"
select MFD_CORE
depends on I2C
depends on I2C && GENERIC_HARDIRQS
help
If you say yes here you get support for the TPS6507x series of
Power Management / Touch Screen chips. These include voltage
......@@ -204,7 +208,7 @@ config TPS6507X
config MFD_TPS65217
tristate "TPS65217 Power Management / White LED chips"
depends on I2C
depends on I2C && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
help
......@@ -234,7 +238,7 @@ config MFD_TPS6586X
config MFD_TPS65910
bool "TPS65910 Power Management chip"
depends on I2C=y && GPIOLIB
depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
......@@ -251,7 +255,7 @@ config MFD_TPS65912_I2C
bool "TPS65912 Power Management chip with I2C"
select MFD_CORE
select MFD_TPS65912
depends on I2C=y && GPIOLIB
depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
help
If you say yes here you get support for the TPS65912 series of
PM chips with I2C interface.
......@@ -260,7 +264,7 @@ config MFD_TPS65912_SPI
bool "TPS65912 Power Management chip with SPI"
select MFD_CORE
select MFD_TPS65912
depends on SPI_MASTER && GPIOLIB
depends on SPI_MASTER && GPIOLIB && GENERIC_HARDIRQS
help
If you say yes here you get support for the TPS65912 series of
PM chips with SPI interface.
......@@ -330,13 +334,13 @@ config TWL4030_POWER
config MFD_TWL4030_AUDIO
bool
depends on TWL4030_CORE
depends on TWL4030_CORE && GENERIC_HARDIRQS
select MFD_CORE
default n
config TWL6040_CORE
bool "Support for TWL6040 audio codec"
depends on I2C=y
depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
......@@ -405,7 +409,7 @@ config MFD_TMIO
config MFD_T7L66XB
bool "Support Toshiba T7L66XB"
depends on ARM && HAVE_CLK
depends on ARM && HAVE_CLK && GENERIC_HARDIRQS
select MFD_CORE
select MFD_TMIO
help
......@@ -413,7 +417,7 @@ config MFD_T7L66XB
config MFD_SMSC
bool "Support for the SMSC ECE1099 series chips"
depends on I2C=y
depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
help
......@@ -460,7 +464,7 @@ config MFD_DA9052_SPI
select REGMAP_SPI
select REGMAP_IRQ
select PMIC_DA9052
depends on SPI_MASTER=y
depends on SPI_MASTER=y && GENERIC_HARDIRQS
help
Support for the Dialog Semiconductor DA9052 PMIC
when controlled using SPI. This driver provides common support
......@@ -472,7 +476,7 @@ config MFD_DA9052_I2C
select REGMAP_I2C
select REGMAP_IRQ
select PMIC_DA9052
depends on I2C=y
depends on I2C=y && GENERIC_HARDIRQS
help
Support for the Dialog Semiconductor DA9052 PMIC
when controlled using I2C. This driver provides common support
......@@ -485,7 +489,7 @@ config MFD_DA9055
select REGMAP_IRQ
select PMIC_DA9055
select MFD_CORE
depends on I2C=y
depends on I2C=y && GENERIC_HARDIRQS
help
Say yes here for support of Dialog Semiconductor DA9055. This is
a Power Management IC. This driver provides common support for
......@@ -508,7 +512,7 @@ config PMIC_ADP5520
config MFD_LP8788
bool "Texas Instruments LP8788 Power Management Unit Driver"
depends on I2C=y
depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
select IRQ_DOMAIN
......@@ -611,7 +615,7 @@ config MFD_ARIZONA_I2C
select MFD_ARIZONA
select MFD_CORE
select REGMAP_I2C
depends on I2C
depends on I2C && GENERIC_HARDIRQS
help
Support for the Wolfson Microelectronics Arizona platform audio SoC
core functionality controlled via I2C.
......@@ -621,7 +625,7 @@ config MFD_ARIZONA_SPI
select MFD_ARIZONA
select MFD_CORE
select REGMAP_SPI
depends on SPI_MASTER
depends on SPI_MASTER && GENERIC_HARDIRQS
help
Support for the Wolfson Microelectronics Arizona platform audio SoC
core functionality controlled via I2C.
......@@ -641,7 +645,7 @@ config MFD_WM5110
config MFD_WM8400
bool "Support Wolfson Microelectronics WM8400"
select MFD_CORE
depends on I2C=y
depends on I2C=y && GENERIC_HARDIRQS
select REGMAP_I2C
help
Support for the Wolfson Microelecronics WM8400 PMIC and audio
......@@ -785,7 +789,7 @@ config MFD_MC13783
config MFD_MC13XXX
tristate
depends on SPI_MASTER || I2C
depends on (SPI_MASTER || I2C) && GENERIC_HARDIRQS
select MFD_CORE
select MFD_MC13783
help
......@@ -796,7 +800,7 @@ config MFD_MC13XXX
config MFD_MC13XXX_SPI
tristate "Freescale MC13783 and MC13892 SPI interface"
depends on SPI_MASTER
depends on SPI_MASTER && GENERIC_HARDIRQS
select REGMAP_SPI
select MFD_MC13XXX
help
......@@ -804,7 +808,7 @@ config MFD_MC13XXX_SPI
config MFD_MC13XXX_I2C
tristate "Freescale MC13892 I2C interface"
depends on I2C
depends on I2C && GENERIC_HARDIRQS
select REGMAP_I2C
select MFD_MC13XXX
help
......@@ -822,7 +826,7 @@ config ABX500_CORE
config AB3100_CORE
bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
depends on I2C=y && ABX500_CORE
depends on I2C=y && ABX500_CORE && GENERIC_HARDIRQS
select MFD_CORE
default y if ARCH_U300
help
......@@ -909,7 +913,7 @@ config MFD_TIMBERDALE
config LPC_SCH
tristate "Intel SCH LPC"
depends on PCI
depends on PCI && GENERIC_HARDIRQS
select MFD_CORE
help
LPC bridge function of the Intel SCH provides support for
......@@ -917,7 +921,7 @@ config LPC_SCH
config LPC_ICH
tristate "Intel ICH LPC"
depends on PCI
depends on PCI && GENERIC_HARDIRQS
select MFD_CORE
help
The LPC bridge function of the Intel ICH provides support for
......@@ -928,7 +932,7 @@ config LPC_ICH
config MFD_RDC321X
tristate "Support for RDC-R321x southbridge"
select MFD_CORE
depends on PCI
depends on PCI && GENERIC_HARDIRQS
help
Say yes here if you want to have support for the RDC R-321x SoC
southbridge which provides access to GPIOs and Watchdog using the
......@@ -937,7 +941,7 @@ config MFD_RDC321X
config MFD_JANZ_CMODIO
tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board"
select MFD_CORE
depends on PCI
depends on PCI && GENERIC_HARDIRQS
help
This is the core driver for the Janz CMOD-IO PCI MODULbus
carrier board. This device is a PCI to MODULbus bridge which may
......@@ -955,7 +959,7 @@ config MFD_JZ4740_ADC
config MFD_VX855
tristate "Support for VIA VX855/VX875 integrated south bridge"
depends on PCI
depends on PCI && GENERIC_HARDIRQS
select MFD_CORE
help
Say yes here to enable support for various functions of the
......@@ -964,7 +968,7 @@ config MFD_VX855
config MFD_WL1273_CORE
tristate "Support for TI WL1273 FM radio."
depends on I2C
depends on I2C && GENERIC_HARDIRQS
select MFD_CORE
default n
help
......@@ -1028,7 +1032,7 @@ config MFD_TPS65090
config MFD_AAT2870_CORE
bool "Support for the AnalogicTech AAT2870"
select MFD_CORE
depends on I2C=y && GPIOLIB
depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
help
If you say yes here you get support for the AAT2870.
This driver provides common support for accessing the device,
......@@ -1060,7 +1064,7 @@ config MFD_RC5T583
config MFD_STA2X11
bool "STA2X11 multi function device support"
depends on STA2X11
depends on STA2X11 && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_MMIO
......@@ -1077,7 +1081,7 @@ config MFD_PALMAS
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
depends on I2C=y
depends on I2C=y && GENERIC_HARDIRQS
help
If you say yes here you get support for the Palmas
series of PMIC chips from Texas Instruments.
......@@ -1085,7 +1089,7 @@ config MFD_PALMAS
config MFD_VIPERBOARD
tristate "Support for Nano River Technologies Viperboard"
select MFD_CORE
depends on USB
depends on USB && GENERIC_HARDIRQS
default n
help
Say yes here if you want support for Nano River Technologies
......@@ -1099,7 +1103,7 @@ config MFD_VIPERBOARD
config MFD_RETU
tristate "Support for Retu multi-function device"
select MFD_CORE
depends on I2C
depends on I2C && GENERIC_HARDIRQS
select REGMAP_IRQ
help
Retu is a multi-function device found on Nokia Internet Tablets
......@@ -1110,7 +1114,7 @@ config MFD_AS3711
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
depends on I2C=y
depends on I2C=y && GENERIC_HARDIRQS
help
Support for the AS3711 PMIC from AMS
......
......@@ -9,7 +9,7 @@ obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o
rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o
obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
......
......@@ -320,6 +320,7 @@ static struct abx500_ops ab8500_ops = {
.mask_and_set_register = ab8500_mask_and_set_register,
.event_registers_startup_state_get = NULL,
.startup_irq_enabled = NULL,
.dump_all_banks = ab8500_dump_all_banks,
};
static void ab8500_irq_lock(struct irq_data *data)
......@@ -368,16 +369,48 @@ static void ab8500_irq_mask(struct irq_data *data)
int mask = 1 << (offset % 8);
ab8500->mask[index] |= mask;
/* The AB8500 GPIOs have two interrupts each (rising & falling). */
if (offset >= AB8500_INT_GPIO6R && offset <= AB8500_INT_GPIO41R)
ab8500->mask[index + 2] |= mask;
if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R)
ab8500->mask[index + 1] |= mask;
if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R)
/* Here the falling IRQ is one bit lower */
ab8500->mask[index] |= (mask << 1);
}
static void ab8500_irq_unmask(struct irq_data *data)
{
struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
unsigned int type = irqd_get_trigger_type(data);
int offset = data->hwirq;
int index = offset / 8;
int mask = 1 << (offset % 8);
ab8500->mask[index] &= ~mask;
if (type & IRQ_TYPE_EDGE_RISING)
ab8500->mask[index] &= ~mask;
/* The AB8500 GPIOs have two interrupts each (rising & falling). */
if (type & IRQ_TYPE_EDGE_FALLING) {
if (offset >= AB8500_INT_GPIO6R && offset <= AB8500_INT_GPIO41R)
ab8500->mask[index + 2] &= ~mask;
else if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R)
ab8500->mask[index + 1] &= ~mask;
else if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R)
/* Here the falling IRQ is one bit lower */
ab8500->mask[index] &= ~(mask << 1);
else
ab8500->mask[index] &= ~mask;
} else {
/* Satisfies the case where type is not set. */
ab8500->mask[index] &= ~mask;
}
}
static int ab8500_irq_set_type(struct irq_data *data, unsigned int type)
{
return 0;
}
static struct irq_chip ab8500_irq_chip = {
......@@ -387,6 +420,7 @@ static struct irq_chip ab8500_irq_chip = {
.irq_mask = ab8500_irq_mask,
.irq_disable = ab8500_irq_mask,
.irq_unmask = ab8500_irq_unmask,
.irq_set_type = ab8500_irq_set_type,
};
static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,
......@@ -411,6 +445,19 @@ static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,
line = (i << 3) + int_bit;
latch_val &= ~(1 << int_bit);
/*
* This handles the falling edge hwirqs from the GPIO
* lines. Route them back to the line registered for the
* rising IRQ, as this is merely a flag for the same IRQ
* in linux terms.
*/
if (line >= AB8500_INT_GPIO6F && line <= AB8500_INT_GPIO41F)
line -= 16;
if (line >= AB9540_INT_GPIO50F && line <= AB9540_INT_GPIO54F)
line -= 8;
if (line == AB8540_INT_GPIO43F || line == AB8540_INT_GPIO44F)
line += 1;
handle_nested_irq(ab8500->irq_base + line);
} while (latch_val);
......@@ -521,6 +568,7 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
int virq = ab8500_irq_get_virq(ab8500, line);
handle_nested_irq(virq);
ab8500_debug_register_interrupt(line);
value &= ~(1 << bit);
} while (value);
......@@ -929,7 +977,7 @@ static struct resource ab8505_iddet_resources[] = {
static struct resource ab8500_temp_resources[] = {
{
.name = "AB8500_TEMP_WARM",
.name = "ABX500_TEMP_WARM",
.start = AB8500_INT_TEMP_WARM,
.end = AB8500_INT_TEMP_WARM,
.flags = IORESOURCE_IRQ,
......@@ -1005,8 +1053,8 @@ static struct mfd_cell abx500_common_devs[] = {
.of_compatible = "stericsson,ab8500-denc",
},
{
.name = "ab8500-temp",
.of_compatible = "stericsson,ab8500-temp",
.name = "abx500-temp",
.of_compatible = "stericsson,abx500-temp",
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
.resources = ab8500_temp_resources,
},
......@@ -1049,7 +1097,7 @@ static struct mfd_cell ab8500_bm_devs[] = {
static struct mfd_cell ab8500_devs[] = {
{
.name = "ab8500-gpio",
.name = "pinctrl-ab8500",
.of_compatible = "stericsson,ab8500-gpio",
},
{
......@@ -1066,7 +1114,8 @@ static struct mfd_cell ab8500_devs[] = {
static struct mfd_cell ab9540_devs[] = {
{
.name = "ab8500-gpio",
.name = "pinctrl-ab9540",
.of_compatible = "stericsson,ab9540-gpio",
},
{
.name = "ab9540-usb",
......
......@@ -4,6 +4,72 @@
* Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
* License Terms: GNU General Public License v2
*/
/*
* AB8500 register access
* ======================
*
* read:
* # echo BANK > <debugfs>/ab8500/register-bank
* # echo ADDR > <debugfs>/ab8500/register-address
* # cat <debugfs>/ab8500/register-value
*
* write:
* # echo BANK > <debugfs>/ab8500/register-bank
* # echo ADDR > <debugfs>/ab8500/register-address
* # echo VALUE > <debugfs>/ab8500/register-value
*
* read all registers from a bank:
* # echo BANK > <debugfs>/ab8500/register-bank
* # cat <debugfs>/ab8500/all-bank-register
*
* BANK target AB8500 register bank
* ADDR target AB8500 register address
* VALUE decimal or 0x-prefixed hexadecimal
*
*
* User Space notification on AB8500 IRQ
* =====================================
*
* Allows user space entity to be notified when target AB8500 IRQ occurs.
* When subscribed, a sysfs entry is created in ab8500.i2c platform device.
* One can pool this file to get target IRQ occurence information.
*
* subscribe to an AB8500 IRQ:
* # echo IRQ > <debugfs>/ab8500/irq-subscribe
*
* unsubscribe from an AB8500 IRQ:
* # echo IRQ > <debugfs>/ab8500/irq-unsubscribe
*
*
* AB8500 register formated read/write access
* ==========================================
*
* Read: read data, data>>SHIFT, data&=MASK, output data
* [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE
* Write: read data, data &= ~(MASK<<SHIFT), data |= (VALUE<<SHIFT), write data
* [0xABCDEF98] shift=12 mask=0xFFF value=0x123 => [0xAB123F98]
*
* Usage:
* # echo "CMD [OPTIONS] BANK ADRESS [VALUE]" > $debugfs/ab8500/hwreg
*
* CMD read read access
* write write access
*
* BANK target reg bank
* ADDRESS target reg address
* VALUE (write) value to be updated
*
* OPTIONS
* -d|-dec (read) output in decimal
* -h|-hexa (read) output in 0x-hexa (default)
* -l|-w|-b 32bit (default), 16bit or 8bit reg access
* -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF)
* -s|-shift SHIFT bit shift value (read:left, write:right)
* -o|-offset OFFSET address offset to add to ADDRESS value
*
* Warning: bit shift operation is applied to bit-mask.
* Warning: bit shift direction depends on read or right command.
*/
#include <linux/seq_file.h>
#include <linux/uaccess.h>
......@@ -11,13 +77,30 @@
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-gpadc.h>
#ifdef CONFIG_DEBUG_FS
#include <linux/string.h>
#include <linux/ctype.h>
#endif
static u32 debug_bank;
static u32 debug_address;
static int irq_first;
static int irq_last;
static u32 *irq_count;
static int num_irqs;
static struct device_attribute **dev_attr;
static char **event_name;
/**
* struct ab8500_reg_range
* @first: the first address of the range
......@@ -42,15 +125,35 @@ struct ab8500_prcmu_ranges {
const struct ab8500_reg_range *range;
};
/* hwreg- "mask" and "shift" entries ressources */
struct hwreg_cfg {
u32 bank; /* target bank */
u32 addr; /* target address */
uint fmt; /* format */
uint mask; /* read/write mask, applied before any bit shift */
int shift; /* bit shift (read:right shift, write:left shift */
};
/* fmt bit #0: 0=hexa, 1=dec */
#define REG_FMT_DEC(c) ((c)->fmt & 0x1)
#define REG_FMT_HEX(c) (!REG_FMT_DEC(c))
static struct hwreg_cfg hwreg_cfg = {
.addr = 0, /* default: invalid phys addr */
.fmt = 0, /* default: 32bit access, hex output */
.mask = 0xFFFFFFFF, /* default: no mask */
.shift = 0, /* default: no bit shift */
};
#define AB8500_NAME_STRING "ab8500"
#define AB8500_NUM_BANKS 22
#define AB8500_ADC_NAME_STRING "gpadc"
#define AB8500_NUM_BANKS 24
#define AB8500_REV_REG 0x80
static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
[0x0] = {
.num_ranges = 0,
.range = 0,
.range = NULL,
},
[AB8500_SYS_CTRL1_BLOCK] = {
.num_ranges = 3,
......@@ -215,7 +318,7 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
},
[AB8500_CHARGER] = {
.num_ranges = 8,
.num_ranges = 9,
.range = (struct ab8500_reg_range[]) {
{
.first = 0x00,
......@@ -249,6 +352,10 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
.first = 0xC0,
.last = 0xC2,
},
{
.first = 0xf5,
.last = 0xf6,
},
},
},
[AB8500_GAS_GAUGE] = {
......@@ -268,6 +375,24 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
},
},
[AB8500_DEVELOPMENT] = {
.num_ranges = 1,
.range = (struct ab8500_reg_range[]) {
{
.first = 0x00,
.last = 0x00,
},
},
},
[AB8500_DEBUG] = {
.num_ranges = 1,
.range = (struct ab8500_reg_range[]) {
{
.first = 0x05,
.last = 0x07,
},
},
},
[AB8500_AUDIO] = {
.num_ranges = 1,
.range = (struct ab8500_reg_range[]) {
......@@ -354,15 +479,30 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
};
static int ab8500_registers_print(struct seq_file *s, void *p)
static irqreturn_t ab8500_debug_handler(int irq, void *data)
{
struct device *dev = s->private;
unsigned int i;
u32 bank = debug_bank;
char buf[16];
struct kobject *kobj = (struct kobject *)data;
unsigned int irq_abb = irq - irq_first;
seq_printf(s, AB8500_NAME_STRING " register values:\n");
if (irq_abb < num_irqs)
irq_count[irq_abb]++;
/*
* This makes it possible to use poll for events (POLLPRI | POLLERR)
* from userspace on sysfs file named <irq-nr>
*/
sprintf(buf, "%d", irq);
sysfs_notify(kobj, NULL, buf);
return IRQ_HANDLED;
}
/* Prints to seq_file or log_buf */
static int ab8500_registers_print(struct device *dev, u32 bank,
struct seq_file *s)
{
unsigned int i;
seq_printf(s, " bank %u:\n", bank);
for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
u32 reg;
......@@ -379,22 +519,42 @@ static int ab8500_registers_print(struct seq_file *s, void *p)
return err;
}
err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n", bank,
reg, value);
if (err < 0) {
dev_err(dev, "seq_printf overflow\n");
/* Error is not returned here since
* the output is wanted in any case */
return 0;
if (s) {
err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n",
bank, reg, value);
if (err < 0) {
dev_err(dev,
"seq_printf overflow bank=%d reg=%d\n",
bank, reg);
/* Error is not returned here since
* the output is wanted in any case */
return 0;
}
} else {
printk(KERN_INFO" [%u/0x%02X]: 0x%02X\n", bank,
reg, value);
}
}
}
return 0;
}
static int ab8500_print_bank_registers(struct seq_file *s, void *p)
{
struct device *dev = s->private;
u32 bank = debug_bank;
seq_printf(s, AB8500_NAME_STRING " register values:\n");
seq_printf(s, " bank %u:\n", bank);
ab8500_registers_print(dev, bank, s);
return 0;
}
static int ab8500_registers_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_registers_print, inode->i_private);
return single_open(file, ab8500_print_bank_registers, inode->i_private);
}
static const struct file_operations ab8500_registers_fops = {
......@@ -405,6 +565,64 @@ static const struct file_operations ab8500_registers_fops = {
.owner = THIS_MODULE,
};
static int ab8500_print_all_banks(struct seq_file *s, void *p)
{
struct device *dev = s->private;
unsigned int i;
int err;
seq_printf(s, AB8500_NAME_STRING " register values:\n");
for (i = 1; i < AB8500_NUM_BANKS; i++) {
err = seq_printf(s, " bank %u:\n", i);
if (err < 0)
dev_err(dev, "seq_printf overflow, bank=%d\n", i);
ab8500_registers_print(dev, i, s);
}
return 0;
}
/* Dump registers to kernel log */
void ab8500_dump_all_banks(struct device *dev)
{
unsigned int i;
printk(KERN_INFO"ab8500 register values:\n");
for (i = 1; i < AB8500_NUM_BANKS; i++) {
printk(KERN_INFO" bank %u:\n", i);
ab8500_registers_print(dev, i, NULL);
}
}
static int ab8500_all_banks_open(struct inode *inode, struct file *file)
{
struct seq_file *s;
int err;
err = single_open(file, ab8500_print_all_banks, inode->i_private);
if (!err) {
/* Default buf size in seq_read is not enough */
s = (struct seq_file *)file->private_data;
s->size = (PAGE_SIZE * 2);
s->buf = kmalloc(s->size, GFP_KERNEL);
if (!s->buf) {
single_release(inode, file);
err = -ENOMEM;
}
}
return err;
}
static const struct file_operations ab8500_all_banks_fops = {
.open = ab8500_all_banks_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_bank_print(struct seq_file *s, void *p)
{
return seq_printf(s, "%d\n", debug_bank);
......@@ -519,6 +737,761 @@ static ssize_t ab8500_val_write(struct file *file,
return count;
}
/*
* Interrupt status
*/
static u32 num_interrupts[AB8500_MAX_NR_IRQS];
static int num_interrupt_lines;
void ab8500_debug_register_interrupt(int line)
{
if (line < num_interrupt_lines)
num_interrupts[line]++;
}
static int ab8500_interrupts_print(struct seq_file *s, void *p)
{
int line;
seq_printf(s, "irq: number of\n");
for (line = 0; line < num_interrupt_lines; line++)
seq_printf(s, "%3i: %6i\n", line, num_interrupts[line]);
return 0;
}
static int ab8500_interrupts_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_interrupts_print, inode->i_private);
}
/*
* - HWREG DB8500 formated routines
*/
static int ab8500_hwreg_print(struct seq_file *s, void *d)
{
struct device *dev = s->private;
int ret;
u8 regvalue;
ret = abx500_get_register_interruptible(dev,
(u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, &regvalue);
if (ret < 0) {
dev_err(dev, "abx500_get_reg fail %d, %d\n",
ret, __LINE__);
return -EINVAL;
}
if (hwreg_cfg.shift >= 0)
regvalue >>= hwreg_cfg.shift;
else
regvalue <<= -hwreg_cfg.shift;
regvalue &= hwreg_cfg.mask;
if (REG_FMT_DEC(&hwreg_cfg))
seq_printf(s, "%d\n", regvalue);
else
seq_printf(s, "0x%02X\n", regvalue);
return 0;
}
static int ab8500_hwreg_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_hwreg_print, inode->i_private);
}
static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p)
{
int bat_ctrl_raw;
int bat_ctrl_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL);
bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc,
BAT_CTRL, bat_ctrl_raw);
return seq_printf(s, "%d,0x%X\n",
bat_ctrl_convert, bat_ctrl_raw);
}
static int ab8500_gpadc_bat_ctrl_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_bat_ctrl_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_bat_ctrl_fops = {
.open = ab8500_gpadc_bat_ctrl_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p)
{
int btemp_ball_raw;
int btemp_ball_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL);
btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL,
btemp_ball_raw);
return seq_printf(s,
"%d,0x%X\n", btemp_ball_convert, btemp_ball_raw);
}
static int ab8500_gpadc_btemp_ball_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_btemp_ball_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_btemp_ball_fops = {
.open = ab8500_gpadc_btemp_ball_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p)
{
int main_charger_v_raw;
int main_charger_v_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V);
main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
MAIN_CHARGER_V, main_charger_v_raw);
return seq_printf(s, "%d,0x%X\n",
main_charger_v_convert, main_charger_v_raw);
}
static int ab8500_gpadc_main_charger_v_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_main_charger_v_print,
inode->i_private);
}
static const struct file_operations ab8500_gpadc_main_charger_v_fops = {
.open = ab8500_gpadc_main_charger_v_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p)
{
int acc_detect1_raw;
int acc_detect1_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1);
acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1,
acc_detect1_raw);
return seq_printf(s, "%d,0x%X\n",
acc_detect1_convert, acc_detect1_raw);
}
static int ab8500_gpadc_acc_detect1_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_acc_detect1_print,
inode->i_private);
}
static const struct file_operations ab8500_gpadc_acc_detect1_fops = {
.open = ab8500_gpadc_acc_detect1_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p)
{
int acc_detect2_raw;
int acc_detect2_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2);
acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc,
ACC_DETECT2, acc_detect2_raw);
return seq_printf(s, "%d,0x%X\n",
acc_detect2_convert, acc_detect2_raw);
}
static int ab8500_gpadc_acc_detect2_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_acc_detect2_print,
inode->i_private);
}
static const struct file_operations ab8500_gpadc_acc_detect2_fops = {
.open = ab8500_gpadc_acc_detect2_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p)
{
int aux1_raw;
int aux1_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1);
aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1,
aux1_raw);
return seq_printf(s, "%d,0x%X\n",
aux1_convert, aux1_raw);
}
static int ab8500_gpadc_aux1_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_aux1_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_aux1_fops = {
.open = ab8500_gpadc_aux1_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p)
{
int aux2_raw;
int aux2_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2);
aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2,
aux2_raw);
return seq_printf(s, "%d,0x%X\n",
aux2_convert, aux2_raw);
}
static int ab8500_gpadc_aux2_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_aux2_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_aux2_fops = {
.open = ab8500_gpadc_aux2_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p)
{
int main_bat_v_raw;
int main_bat_v_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V);
main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V,
main_bat_v_raw);
return seq_printf(s, "%d,0x%X\n",
main_bat_v_convert, main_bat_v_raw);
}
static int ab8500_gpadc_main_bat_v_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_main_bat_v_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_main_bat_v_fops = {
.open = ab8500_gpadc_main_bat_v_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p)
{
int vbus_v_raw;
int vbus_v_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V);
vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V,
vbus_v_raw);
return seq_printf(s, "%d,0x%X\n",
vbus_v_convert, vbus_v_raw);
}
static int ab8500_gpadc_vbus_v_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_vbus_v_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_vbus_v_fops = {
.open = ab8500_gpadc_vbus_v_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p)
{
int main_charger_c_raw;
int main_charger_c_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C);
main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
MAIN_CHARGER_C, main_charger_c_raw);
return seq_printf(s, "%d,0x%X\n",
main_charger_c_convert, main_charger_c_raw);
}
static int ab8500_gpadc_main_charger_c_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_main_charger_c_print,
inode->i_private);
}
static const struct file_operations ab8500_gpadc_main_charger_c_fops = {
.open = ab8500_gpadc_main_charger_c_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p)
{
int usb_charger_c_raw;
int usb_charger_c_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C);
usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
USB_CHARGER_C, usb_charger_c_raw);
return seq_printf(s, "%d,0x%X\n",
usb_charger_c_convert, usb_charger_c_raw);
}
static int ab8500_gpadc_usb_charger_c_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_usb_charger_c_print,
inode->i_private);
}
static const struct file_operations ab8500_gpadc_usb_charger_c_fops = {
.open = ab8500_gpadc_usb_charger_c_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p)
{
int bk_bat_v_raw;
int bk_bat_v_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V);
bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
BK_BAT_V, bk_bat_v_raw);
return seq_printf(s, "%d,0x%X\n",
bk_bat_v_convert, bk_bat_v_raw);
}
static int ab8500_gpadc_bk_bat_v_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_bk_bat_v_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_bk_bat_v_fops = {
.open = ab8500_gpadc_bk_bat_v_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p)
{
int die_temp_raw;
int die_temp_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP);
die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP,
die_temp_raw);
return seq_printf(s, "%d,0x%X\n",
die_temp_convert, die_temp_raw);
}
static int ab8500_gpadc_die_temp_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_die_temp_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_die_temp_fops = {
.open = ab8500_gpadc_die_temp_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
/*
* return length of an ASCII numerical value, 0 is string is not a
* numerical value.
* string shall start at value 1st char.
* string can be tailed with \0 or space or newline chars only.
* value can be decimal or hexadecimal (prefixed 0x or 0X).
*/
static int strval_len(char *b)
{
char *s = b;
if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) {
s += 2;
for (; *s && (*s != ' ') && (*s != '\n'); s++) {
if (!isxdigit(*s))
return 0;
}
} else {
if (*s == '-')
s++;
for (; *s && (*s != ' ') && (*s != '\n'); s++) {
if (!isdigit(*s))
return 0;
}
}
return (int) (s-b);
}
/*
* parse hwreg input data.
* update global hwreg_cfg only if input data syntax is ok.
*/
static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg,
struct device *dev)
{
uint write, val = 0;
u8 regvalue;
int ret;
struct hwreg_cfg loc = {
.bank = 0, /* default: invalid phys addr */
.addr = 0, /* default: invalid phys addr */
.fmt = 0, /* default: 32bit access, hex output */
.mask = 0xFFFFFFFF, /* default: no mask */
.shift = 0, /* default: no bit shift */
};
/* read or write ? */
if (!strncmp(b, "read ", 5)) {
write = 0;
b += 5;
} else if (!strncmp(b, "write ", 6)) {
write = 1;
b += 6;
} else
return -EINVAL;
/* OPTIONS -l|-w|-b -s -m -o */
while ((*b == ' ') || (*b == '-')) {
if (*(b-1) != ' ') {
b++;
continue;
}
if ((!strncmp(b, "-d ", 3)) ||
(!strncmp(b, "-dec ", 5))) {
b += (*(b+2) == ' ') ? 3 : 5;
loc.fmt |= (1<<0);
} else if ((!strncmp(b, "-h ", 3)) ||
(!strncmp(b, "-hex ", 5))) {
b += (*(b+2) == ' ') ? 3 : 5;
loc.fmt &= ~(1<<0);
} else if ((!strncmp(b, "-m ", 3)) ||
(!strncmp(b, "-mask ", 6))) {
b += (*(b+2) == ' ') ? 3 : 6;
if (strval_len(b) == 0)
return -EINVAL;
loc.mask = simple_strtoul(b, &b, 0);
} else if ((!strncmp(b, "-s ", 3)) ||
(!strncmp(b, "-shift ", 7))) {
b += (*(b+2) == ' ') ? 3 : 7;
if (strval_len(b) == 0)
return -EINVAL;
loc.shift = simple_strtol(b, &b, 0);
} else {
return -EINVAL;
}
}
/* get arg BANK and ADDRESS */
if (strval_len(b) == 0)
return -EINVAL;
loc.bank = simple_strtoul(b, &b, 0);
while (*b == ' ')
b++;
if (strval_len(b) == 0)
return -EINVAL;
loc.addr = simple_strtoul(b, &b, 0);
if (write) {
while (*b == ' ')
b++;
if (strval_len(b) == 0)
return -EINVAL;
val = simple_strtoul(b, &b, 0);
}
/* args are ok, update target cfg (mainly for read) */
*cfg = loc;
#ifdef ABB_HWREG_DEBUG
pr_warn("HWREG request: %s, %s, addr=0x%08X, mask=0x%X, shift=%d"
"value=0x%X\n", (write) ? "write" : "read",
REG_FMT_DEC(cfg) ? "decimal" : "hexa",
cfg->addr, cfg->mask, cfg->shift, val);
#endif
if (!write)
return 0;
ret = abx500_get_register_interruptible(dev,
(u8)cfg->bank, (u8)cfg->addr, &regvalue);
if (ret < 0) {
dev_err(dev, "abx500_get_reg fail %d, %d\n",
ret, __LINE__);
return -EINVAL;
}
if (cfg->shift >= 0) {
regvalue &= ~(cfg->mask << (cfg->shift));
val = (val & cfg->mask) << (cfg->shift);
} else {
regvalue &= ~(cfg->mask >> (-cfg->shift));
val = (val & cfg->mask) >> (-cfg->shift);
}
val = val | regvalue;
ret = abx500_set_register_interruptible(dev,
(u8)cfg->bank, (u8)cfg->addr, (u8)val);
if (ret < 0) {
pr_err("abx500_set_reg failed %d, %d", ret, __LINE__);
return -EINVAL;
}
return 0;
}
static ssize_t ab8500_hwreg_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[128];
int buf_size, ret;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
/* get args and process */
ret = hwreg_common_write(buf, &hwreg_cfg, dev);
return (ret) ? ret : buf_size;
}
/*
* - irq subscribe/unsubscribe stuff
*/
static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p)
{
seq_printf(s, "%d\n", irq_first);
return 0;
}
static int ab8500_subscribe_unsubscribe_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_subscribe_unsubscribe_print,
inode->i_private);
}
/*
* Userspace should use poll() on this file. When an event occur
* the blocking poll will be released.
*/
static ssize_t show_irq(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long name;
unsigned int irq_index;
int err;
err = strict_strtoul(attr->attr.name, 0, &name);
if (err)
return err;
irq_index = name - irq_first;
if (irq_index >= num_irqs)
return -EINVAL;
else
return sprintf(buf, "%u\n", irq_count[irq_index]);
}
static ssize_t ab8500_subscribe_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_val;
int err;
unsigned int irq_index;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_val);
if (err)
return -EINVAL;
if (user_val < irq_first) {
dev_err(dev, "debugfs error input < %d\n", irq_first);
return -EINVAL;
}
if (user_val > irq_last) {
dev_err(dev, "debugfs error input > %d\n", irq_last);
return -EINVAL;
}
irq_index = user_val - irq_first;
if (irq_index >= num_irqs)
return -EINVAL;
/*
* This will create a sysfs file named <irq-nr> which userspace can
* use to select or poll and get the AB8500 events
*/
dev_attr[irq_index] = kmalloc(sizeof(struct device_attribute),
GFP_KERNEL);
event_name[irq_index] = kmalloc(buf_size, GFP_KERNEL);
sprintf(event_name[irq_index], "%lu", user_val);
dev_attr[irq_index]->show = show_irq;
dev_attr[irq_index]->store = NULL;
dev_attr[irq_index]->attr.name = event_name[irq_index];
dev_attr[irq_index]->attr.mode = S_IRUGO;
err = sysfs_create_file(&dev->kobj, &dev_attr[irq_index]->attr);
if (err < 0) {
printk(KERN_ERR "sysfs_create_file failed %d\n", err);
return err;
}
err = request_threaded_irq(user_val, NULL, ab8500_debug_handler,
IRQF_SHARED | IRQF_NO_SUSPEND,
"ab8500-debug", &dev->kobj);
if (err < 0) {
printk(KERN_ERR "request_threaded_irq failed %d, %lu\n",
err, user_val);
sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
return err;
}
return buf_size;
}
static ssize_t ab8500_unsubscribe_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_val;
int err;
unsigned int irq_index;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_val);
if (err)
return -EINVAL;
if (user_val < irq_first) {
dev_err(dev, "debugfs error input < %d\n", irq_first);
return -EINVAL;
}
if (user_val > irq_last) {
dev_err(dev, "debugfs error input > %d\n", irq_last);
return -EINVAL;
}
irq_index = user_val - irq_first;
if (irq_index >= num_irqs)
return -EINVAL;
/* Set irq count to 0 when unsubscribe */
irq_count[irq_index] = 0;
if (dev_attr[irq_index])
sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
free_irq(user_val, &dev->kobj);
kfree(event_name[irq_index]);
kfree(dev_attr[irq_index]);
return buf_size;
}
/*
* - several deubgfs nodes fops
*/
static const struct file_operations ab8500_bank_fops = {
.open = ab8500_bank_open,
.write = ab8500_bank_write,
......@@ -546,64 +1519,231 @@ static const struct file_operations ab8500_val_fops = {
.owner = THIS_MODULE,
};
static const struct file_operations ab8500_interrupts_fops = {
.open = ab8500_interrupts_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static const struct file_operations ab8500_subscribe_fops = {
.open = ab8500_subscribe_unsubscribe_open,
.write = ab8500_subscribe_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static const struct file_operations ab8500_unsubscribe_fops = {
.open = ab8500_subscribe_unsubscribe_open,
.write = ab8500_unsubscribe_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static const struct file_operations ab8500_hwreg_fops = {
.open = ab8500_hwreg_open,
.write = ab8500_hwreg_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct dentry *ab8500_dir;
static struct dentry *ab8500_reg_file;
static struct dentry *ab8500_bank_file;
static struct dentry *ab8500_address_file;
static struct dentry *ab8500_val_file;
static struct dentry *ab8500_gpadc_dir;
static int ab8500_debug_probe(struct platform_device *plf)
{
struct dentry *file;
int ret = -ENOMEM;
struct ab8500 *ab8500;
debug_bank = AB8500_MISC;
debug_address = AB8500_REV_REG & 0x00FF;
ab8500 = dev_get_drvdata(plf->dev.parent);
num_irqs = ab8500->mask_size;
irq_count = kzalloc(sizeof(*irq_count)*num_irqs, GFP_KERNEL);
if (!irq_count)
return -ENOMEM;
dev_attr = kzalloc(sizeof(*dev_attr)*num_irqs,GFP_KERNEL);
if (!dev_attr)
goto out_freeirq_count;
event_name = kzalloc(sizeof(*event_name)*num_irqs, GFP_KERNEL);
if (!event_name)
goto out_freedev_attr;
irq_first = platform_get_irq_byname(plf, "IRQ_FIRST");
if (irq_first < 0) {
dev_err(&plf->dev, "First irq not found, err %d\n",
irq_first);
ret = irq_first;
goto out_freeevent_name;
}
irq_last = platform_get_irq_byname(plf, "IRQ_LAST");
if (irq_last < 0) {
dev_err(&plf->dev, "Last irq not found, err %d\n",
irq_last);
ret = irq_last;
goto out_freeevent_name;
}
ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
if (!ab8500_dir)
goto exit_no_debugfs;
goto err;
ab8500_gpadc_dir = debugfs_create_dir(AB8500_ADC_NAME_STRING,
ab8500_dir);
if (!ab8500_gpadc_dir)
goto err;
file = debugfs_create_file("all-bank-registers", S_IRUGO,
ab8500_dir, &plf->dev, &ab8500_registers_fops);
if (!file)
goto err;
file = debugfs_create_file("all-banks", S_IRUGO,
ab8500_dir, &plf->dev, &ab8500_all_banks_fops);
if (!file)
goto err;
file = debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_bank_fops);
if (!file)
goto err;
file = debugfs_create_file("register-address", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_address_fops);
if (!file)
goto err;
file = debugfs_create_file("register-value", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_val_fops);
if (!file)
goto err;
ab8500_reg_file = debugfs_create_file("all-bank-registers",
S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops);
if (!ab8500_reg_file)
goto exit_destroy_dir;
file = debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_subscribe_fops);
if (!file)
goto err;
ab8500_bank_file = debugfs_create_file("register-bank",
(S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops);
if (!ab8500_bank_file)
goto exit_destroy_reg;
if (is_ab8500(ab8500))
num_interrupt_lines = AB8500_NR_IRQS;
else if (is_ab8505(ab8500))
num_interrupt_lines = AB8505_NR_IRQS;
else if (is_ab9540(ab8500))
num_interrupt_lines = AB9540_NR_IRQS;
ab8500_address_file = debugfs_create_file("register-address",
(S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev,
&ab8500_address_fops);
if (!ab8500_address_file)
goto exit_destroy_bank;
file = debugfs_create_file("interrupts", (S_IRUGO),
ab8500_dir, &plf->dev, &ab8500_interrupts_fops);
if (!file)
goto err;
ab8500_val_file = debugfs_create_file("register-value",
(S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops);
if (!ab8500_val_file)
goto exit_destroy_address;
file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops);
if (!file)
goto err;
file = debugfs_create_file("hwreg", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_hwreg_fops);
if (!file)
goto err;
file = debugfs_create_file("bat_ctrl", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bat_ctrl_fops);
if (!file)
goto err;
file = debugfs_create_file("btemp_ball", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_btemp_ball_fops);
if (!file)
goto err;
file = debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_v_fops);
if (!file)
goto err;
file = debugfs_create_file("acc_detect1", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect1_fops);
if (!file)
goto err;
file = debugfs_create_file("acc_detect2", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect2_fops);
if (!file)
goto err;
file = debugfs_create_file("adc_aux1", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux1_fops);
if (!file)
goto err;
file = debugfs_create_file("adc_aux2", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux2_fops);
if (!file)
goto err;
file = debugfs_create_file("main_bat_v", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_bat_v_fops);
if (!file)
goto err;
file = debugfs_create_file("vbus_v", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_vbus_v_fops);
if (!file)
goto err;
file = debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_c_fops);
if (!file)
goto err;
file = debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_charger_c_fops);
if (!file)
goto err;
file = debugfs_create_file("bk_bat_v", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bk_bat_v_fops);
if (!file)
goto err;
file = debugfs_create_file("die_temp", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_die_temp_fops);
if (!file)
goto err;
return 0;
exit_destroy_address:
debugfs_remove(ab8500_address_file);
exit_destroy_bank:
debugfs_remove(ab8500_bank_file);
exit_destroy_reg:
debugfs_remove(ab8500_reg_file);
exit_destroy_dir:
debugfs_remove(ab8500_dir);
exit_no_debugfs:
err:
if (ab8500_dir)
debugfs_remove_recursive(ab8500_dir);
dev_err(&plf->dev, "failed to create debugfs entries.\n");
return -ENOMEM;
out_freeevent_name:
kfree(event_name);
out_freedev_attr:
kfree(dev_attr);
out_freeirq_count:
kfree(irq_count);
return ret;
}
static int ab8500_debug_remove(struct platform_device *plf)
{
debugfs_remove(ab8500_val_file);
debugfs_remove(ab8500_address_file);
debugfs_remove(ab8500_bank_file);
debugfs_remove(ab8500_reg_file);
debugfs_remove(ab8500_dir);
debugfs_remove_recursive(ab8500_dir);
kfree(event_name);
kfree(dev_attr);
kfree(irq_count);
return 0;
}
......
......@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
......@@ -82,6 +83,11 @@
/* This is used to not lose precision when dividing to get gain and offset */
#define CALIB_SCALE 1000
/* Time in ms before disabling regulator */
#define GPADC_AUDOSUSPEND_DELAY 1
#define CONVERSION_TIME 500 /* ms */
enum cal_channels {
ADC_INPUT_VMAIN = 0,
ADC_INPUT_BTEMP,
......@@ -102,10 +108,10 @@ struct adc_cal_data {
/**
* struct ab8500_gpadc - AB8500 GPADC device information
* @chip_id ABB chip id
* @dev: pointer to the struct device
* @node: a list of AB8500 GPADCs, hence prepared for
reentrance
* @parent: pointer to the struct ab8500
* @ab8500_gpadc_complete: pointer to the struct completion, to indicate
* the completion of gpadc conversion
* @ab8500_gpadc_lock: structure of type mutex
......@@ -114,9 +120,9 @@ struct adc_cal_data {
* @cal_data array of ADC calibration data structs
*/
struct ab8500_gpadc {
u8 chip_id;
struct device *dev;
struct list_head node;
struct ab8500 *parent;
struct completion ab8500_gpadc_complete;
struct mutex ab8500_gpadc_lock;
struct regulator *regu;
......@@ -282,8 +288,9 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
return -ENODEV;
mutex_lock(&gpadc->ab8500_gpadc_lock);
/* Enable VTVout LDO this is required for GPADC */
regulator_enable(gpadc->regu);
pm_runtime_get_sync(gpadc->dev);
/* Check if ADC is not busy, lock and proceed */
do {
......@@ -332,7 +339,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
EN_BUF | EN_ICHAR);
break;
case BTEMP_BALL:
if (gpadc->chip_id >= AB8500_CUT3P0) {
if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
/* Turn on btemp pull-up on ABB 3.0 */
ret = abx500_mask_and_set_register_interruptible(
gpadc->dev,
......@@ -344,7 +351,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
* Delay might be needed for ABB8500 cut 3.0, if not, remove
* when hardware will be available
*/
msleep(1);
usleep_range(1000, 1000);
break;
}
/* Intentional fallthrough */
......@@ -367,7 +374,8 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
goto out;
}
/* wait for completion of conversion */
if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) {
if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
msecs_to_jiffies(CONVERSION_TIME))) {
dev_err(gpadc->dev,
"timeout: didn't receive GPADC conversion interrupt\n");
ret = -EINVAL;
......@@ -397,8 +405,10 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
goto out;
}
/* Disable VTVout LDO this is required for GPADC */
regulator_disable(gpadc->regu);
pm_runtime_mark_last_busy(gpadc->dev);
pm_runtime_put_autosuspend(gpadc->dev);
mutex_unlock(&gpadc->ab8500_gpadc_lock);
return (high_data << 8) | low_data;
......@@ -412,7 +422,9 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
*/
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
regulator_disable(gpadc->regu);
pm_runtime_put(gpadc->dev);
mutex_unlock(&gpadc->ab8500_gpadc_lock);
dev_err(gpadc->dev,
"gpadc_conversion: Failed to AD convert channel %d\n", channel);
......@@ -571,6 +583,28 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
gpadc->cal_data[ADC_INPUT_VBAT].offset);
}
static int ab8500_gpadc_runtime_suspend(struct device *dev)
{
struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
regulator_disable(gpadc->regu);
return 0;
}
static int ab8500_gpadc_runtime_resume(struct device *dev)
{
struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
regulator_enable(gpadc->regu);
return 0;
}
static int ab8500_gpadc_runtime_idle(struct device *dev)
{
pm_runtime_suspend(dev);
return 0;
}
static int ab8500_gpadc_probe(struct platform_device *pdev)
{
int ret = 0;
......@@ -591,6 +625,7 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
}
gpadc->dev = &pdev->dev;
gpadc->parent = dev_get_drvdata(pdev->dev.parent);
mutex_init(&gpadc->ab8500_gpadc_lock);
/* Initialize completion used to notify completion of conversion */
......@@ -607,14 +642,6 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
goto fail;
}
/* Get Chip ID of the ABB ASIC */
ret = abx500_get_chip_id(gpadc->dev);
if (ret < 0) {
dev_err(gpadc->dev, "failed to get chip ID\n");
goto fail_irq;
}
gpadc->chip_id = (u8) ret;
/* VTVout LDO used to power up ab8500-GPADC */
gpadc->regu = regulator_get(&pdev->dev, "vddadc");
if (IS_ERR(gpadc->regu)) {
......@@ -622,6 +649,16 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
dev_err(gpadc->dev, "failed to get vtvout LDO\n");
goto fail_irq;
}
platform_set_drvdata(pdev, gpadc);
regulator_enable(gpadc->regu);
pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY);
pm_runtime_use_autosuspend(gpadc->dev);
pm_runtime_set_active(gpadc->dev);
pm_runtime_enable(gpadc->dev);
ab8500_gpadc_read_calibration_data(gpadc);
list_add_tail(&gpadc->node, &ab8500_gpadc_list);
dev_dbg(gpadc->dev, "probe success\n");
......@@ -642,19 +679,34 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
list_del(&gpadc->node);
/* remove interrupt - completion of Sw ADC conversion */
free_irq(gpadc->irq, gpadc);
/* disable VTVout LDO that is being used by GPADC */
regulator_put(gpadc->regu);
pm_runtime_get_sync(gpadc->dev);
pm_runtime_disable(gpadc->dev);
regulator_disable(gpadc->regu);
pm_runtime_set_suspended(gpadc->dev);
pm_runtime_put_noidle(gpadc->dev);
kfree(gpadc);
gpadc = NULL;
return 0;
}
static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
ab8500_gpadc_runtime_resume,
ab8500_gpadc_runtime_idle)
};
static struct platform_driver ab8500_gpadc_driver = {
.probe = ab8500_gpadc_probe,
.remove = ab8500_gpadc_remove,
.driver = {
.name = "ab8500-gpadc",
.owner = THIS_MODULE,
.pm = &ab8500_gpadc_pm_ops,
},
};
......
......@@ -7,12 +7,73 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/reboot.h>
#include <linux/signal.h>
#include <linux/power_supply.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-sysctrl.h>
static struct device *sysctrl_dev;
void ab8500_power_off(void)
{
sigset_t old;
sigset_t all;
static char *pss[] = {"ab8500_ac", "ab8500_usb"};
int i;
bool charger_present = false;
union power_supply_propval val;
struct power_supply *psy;
int ret;
/*
* If we have a charger connected and we're powering off,
* reboot into charge-only mode.
*/
for (i = 0; i < ARRAY_SIZE(pss); i++) {
psy = power_supply_get_by_name(pss[i]);
if (!psy)
continue;
ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
if (!ret && val.intval) {
charger_present = true;
break;
}
}
if (!charger_present)
goto shutdown;
/* Check if battery is known */
psy = power_supply_get_by_name("ab8500_btemp");
if (psy) {
ret = psy->get_property(psy, POWER_SUPPLY_PROP_TECHNOLOGY,
&val);
if (!ret && val.intval != POWER_SUPPLY_TECHNOLOGY_UNKNOWN) {
printk(KERN_INFO
"Charger \"%s\" is connected with known battery."
" Rebooting.\n",
pss[i]);
machine_restart("charging");
}
}
shutdown:
sigfillset(&all);
if (!sigprocmask(SIG_BLOCK, &all, &old)) {
(void)ab8500_sysctrl_set(AB8500_STW4500CTRL1,
AB8500_STW4500CTRL1_SWOFF |
AB8500_STW4500CTRL1_SWRESET4500N);
(void)sigprocmask(SIG_SETMASK, &old, NULL);
}
}
static inline bool valid_bank(u8 bank)
{
return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
......@@ -33,6 +94,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
return abx500_get_register_interruptible(sysctrl_dev, bank,
(u8)(reg & 0xFF), value);
}
EXPORT_SYMBOL(ab8500_sysctrl_read);
int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
{
......@@ -48,10 +110,40 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
(u8)(reg & 0xFF), mask, value);
}
EXPORT_SYMBOL(ab8500_sysctrl_write);
static int ab8500_sysctrl_probe(struct platform_device *pdev)
{
struct ab8500_platform_data *plat;
struct ab8500_sysctrl_platform_data *pdata;
sysctrl_dev = &pdev->dev;
plat = dev_get_platdata(pdev->dev.parent);
if (plat->pm_power_off)
pm_power_off = ab8500_power_off;
pdata = plat->sysctrl;
if (pdata) {
int ret, i, j;
for (i = AB8500_SYSCLKREQ1RFCLKBUF;
i <= AB8500_SYSCLKREQ8RFCLKBUF; i++) {
j = i - AB8500_SYSCLKREQ1RFCLKBUF;
ret = ab8500_sysctrl_write(i, 0xff,
pdata->initial_req_buf_config[j]);
dev_dbg(&pdev->dev,
"Setting SysClkReq%dRfClkBuf 0x%X\n",
j + 1,
pdata->initial_req_buf_config[j]);
if (ret < 0) {
dev_err(&pdev->dev,
"unable to set sysClkReq%dRfClkBuf: "
"%d\n", j + 1, ret);
}
}
}
return 0;
}
......
......@@ -153,6 +153,22 @@ int abx500_startup_irq_enabled(struct device *dev, unsigned int irq)
}
EXPORT_SYMBOL(abx500_startup_irq_enabled);
void abx500_dump_all_banks(void)
{
struct abx500_ops *ops;
struct device dummy_child = {0};
struct abx500_device_entry *dev_entry;
list_for_each_entry(dev_entry, &abx500_list, list) {
dummy_child.parent = dev_entry->dev;
ops = &dev_entry->ops;
if ((ops != NULL) && (ops->dump_all_banks != NULL))
ops->dump_all_banks(&dummy_child);
}
}
EXPORT_SYMBOL(abx500_dump_all_banks);
MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
MODULE_DESCRIPTION("ABX500 core driver");
MODULE_LICENSE("GPL");
......@@ -115,7 +115,7 @@ static irqreturn_t arizona_underclocked(int irq, void *data)
if (val & ARIZONA_ADC_UNDERCLOCKED_STS)
dev_err(arizona->dev, "ADC underclocked\n");
if (val & ARIZONA_MIXER_UNDERCLOCKED_STS)
dev_err(arizona->dev, "Mixer underclocked\n");
dev_err(arizona->dev, "Mixer dropped sample\n");
return IRQ_HANDLED;
}
......@@ -263,10 +263,36 @@ static int arizona_runtime_suspend(struct device *dev)
}
#endif
#ifdef CONFIG_PM_SLEEP
static int arizona_resume_noirq(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
dev_dbg(arizona->dev, "Early resume, disabling IRQ\n");
disable_irq(arizona->irq);
return 0;
}
static int arizona_resume(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
dev_dbg(arizona->dev, "Late resume, reenabling IRQ\n");
enable_irq(arizona->irq);
return 0;
}
#endif
const struct dev_pm_ops arizona_pm_ops = {
SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
arizona_runtime_resume,
NULL)
SET_SYSTEM_SLEEP_PM_OPS(NULL, arizona_resume)
#ifdef CONFIG_PM_SLEEP
.resume_noirq = arizona_resume_noirq,
#endif
};
EXPORT_SYMBOL_GPL(arizona_pm_ops);
......@@ -275,19 +301,19 @@ static struct mfd_cell early_devs[] = {
};
static struct mfd_cell wm5102_devs[] = {
{ .name = "arizona-micsupp" },
{ .name = "arizona-extcon" },
{ .name = "arizona-gpio" },
{ .name = "arizona-haptics" },
{ .name = "arizona-micsupp" },
{ .name = "arizona-pwm" },
{ .name = "wm5102-codec" },
};
static struct mfd_cell wm5110_devs[] = {
{ .name = "arizona-micsupp" },
{ .name = "arizona-extcon" },
{ .name = "arizona-gpio" },
{ .name = "arizona-haptics" },
{ .name = "arizona-micsupp" },
{ .name = "arizona-pwm" },
{ .name = "wm5110-codec" },
};
......@@ -484,6 +510,29 @@ int arizona_dev_init(struct arizona *arizona)
goto err_reset;
}
for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) {
if (!arizona->pdata.micbias[i].mV)
continue;
val = (arizona->pdata.micbias[i].mV - 1500) / 100;
val <<= ARIZONA_MICB1_LVL_SHIFT;
if (arizona->pdata.micbias[i].ext_cap)
val |= ARIZONA_MICB1_EXT_CAP;
if (arizona->pdata.micbias[i].discharge)
val |= ARIZONA_MICB1_DISCH;
if (arizona->pdata.micbias[i].fast_start)
val |= ARIZONA_MICB1_RATE;
regmap_update_bits(arizona->regmap,
ARIZONA_MIC_BIAS_CTRL_1 + i,
ARIZONA_MICB1_LVL_MASK |
ARIZONA_MICB1_DISCH |
ARIZONA_MICB1_RATE, val);
}
for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
/* Default for both is 0 so noop with defaults */
val = arizona->pdata.dmic_ref[i]
......
......@@ -60,7 +60,7 @@ static inline bool i2c_safe_reg(unsigned char reg)
* This fix is to follow any read or write with a dummy read to a safe
* register.
*/
int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
{
int val;
......@@ -85,7 +85,6 @@ int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
return 0;
}
EXPORT_SYMBOL(da9052_i2c_fix);
static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
{
......
......@@ -33,6 +33,7 @@
#include <linux/regulator/db8500-prcmu.h>
#include <linux/regulator/machine.h>
#include <linux/cpufreq.h>
#include <linux/platform_data/ux500_wdt.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/db8500-regs.h>
......@@ -2207,21 +2208,25 @@ int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
sleep_auto_off ? A9WDOG_AUTO_OFF_EN :
A9WDOG_AUTO_OFF_DIS);
}
EXPORT_SYMBOL(db8500_prcmu_config_a9wdog);
int db8500_prcmu_enable_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0);
}
EXPORT_SYMBOL(db8500_prcmu_enable_a9wdog);
int db8500_prcmu_disable_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0);
}
EXPORT_SYMBOL(db8500_prcmu_disable_a9wdog);
int db8500_prcmu_kick_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0);
}
EXPORT_SYMBOL(db8500_prcmu_kick_a9wdog);
/*
* timeout is 28 bit, in ms.
......@@ -2239,6 +2244,7 @@ int db8500_prcmu_load_a9wdog(u8 id, u32 timeout)
(u8)((timeout >> 12) & 0xff),
(u8)((timeout >> 20) & 0xff));
}
EXPORT_SYMBOL(db8500_prcmu_load_a9wdog);
/**
* prcmu_abb_read() - Read register value(s) from the ABB.
......@@ -3094,6 +3100,11 @@ static struct resource ab8500_resources[] = {
}
};
static struct ux500_wdt_data db8500_wdt_pdata = {
.timeout = 600, /* 10 minutes */
.has_28_bits_resolution = true,
};
static struct mfd_cell db8500_prcmu_devs[] = {
{
.name = "db8500-prcmu-regulators",
......@@ -3107,6 +3118,12 @@ static struct mfd_cell db8500_prcmu_devs[] = {
.platform_data = &db8500_cpufreq_table,
.pdata_size = sizeof(db8500_cpufreq_table),
},
{
.name = "ux500_wdt",
.platform_data = &db8500_wdt_pdata,
.pdata_size = sizeof(db8500_wdt_pdata),
.id = -1,
},
{
.name = "ab8500-core",
.of_compatible = "stericsson,ab8500",
......
......@@ -50,6 +50,7 @@
* document number TBD : Panther Point
* document number TBD : Lynx Point
* document number TBD : Lynx Point-LP
* document number TBD : Wellsburg
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
......@@ -75,8 +76,10 @@
#define ACPIBASE_GCS_OFF 0x3410
#define ACPIBASE_GCS_END 0x3414
#define GPIOBASE 0x48
#define GPIOCTRL 0x4C
#define GPIOBASE_ICH0 0x58
#define GPIOCTRL_ICH0 0x5C
#define GPIOBASE_ICH6 0x48
#define GPIOCTRL_ICH6 0x4C
#define RCBABASE 0xf0
......@@ -84,8 +87,17 @@
#define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i)
#define wdt_res(b, i) (&wdt_ich_res[(b) + (i)])
static int lpc_ich_acpi_save = -1;
static int lpc_ich_gpio_save = -1;
struct lpc_ich_cfg {
int base;
int ctrl;
int save;
};
struct lpc_ich_priv {
int chipset;
struct lpc_ich_cfg acpi;
struct lpc_ich_cfg gpio;
};
static struct resource wdt_ich_res[] = {
/* ACPI - TCO */
......@@ -194,6 +206,7 @@ enum lpc_chipsets {
LPC_PPT, /* Panther Point */
LPC_LPT, /* Lynx Point */
LPC_LPT_LP, /* Lynx Point-LP */
LPC_WBG, /* Wellsburg */
};
struct lpc_ich_info lpc_chipset_info[] = {
......@@ -474,6 +487,10 @@ struct lpc_ich_info lpc_chipset_info[] = {
.name = "Lynx Point_LP",
.iTCO_version = 2,
},
[LPC_WBG] = {
.name = "Wellsburg",
.iTCO_version = 2,
},
};
/*
......@@ -655,45 +672,82 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = {
{ PCI_VDEVICE(INTEL, 0x9c45), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c46), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c47), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x8d40), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d41), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d42), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d43), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d44), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d45), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d46), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d47), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d48), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d49), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d4a), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d4b), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d4c), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d4d), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d4e), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d4f), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d50), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d51), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d52), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d53), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d54), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d55), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d56), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d57), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d58), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d59), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d5a), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d5b), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d5c), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d5d), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d5e), LPC_WBG},
{ PCI_VDEVICE(INTEL, 0x8d5f), LPC_WBG},
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE(pci, lpc_ich_ids);
static void lpc_ich_restore_config_space(struct pci_dev *dev)
{
if (lpc_ich_acpi_save >= 0) {
pci_write_config_byte(dev, ACPICTRL, lpc_ich_acpi_save);
lpc_ich_acpi_save = -1;
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
if (priv->acpi.save >= 0) {
pci_write_config_byte(dev, priv->acpi.ctrl, priv->acpi.save);
priv->acpi.save = -1;
}
if (lpc_ich_gpio_save >= 0) {
pci_write_config_byte(dev, GPIOCTRL, lpc_ich_gpio_save);
lpc_ich_gpio_save = -1;
if (priv->gpio.save >= 0) {
pci_write_config_byte(dev, priv->gpio.ctrl, priv->gpio.save);
priv->gpio.save = -1;
}
}
static void lpc_ich_enable_acpi_space(struct pci_dev *dev)
{
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u8 reg_save;
pci_read_config_byte(dev, ACPICTRL, &reg_save);
pci_write_config_byte(dev, ACPICTRL, reg_save | 0x10);
lpc_ich_acpi_save = reg_save;
pci_read_config_byte(dev, priv->acpi.ctrl, &reg_save);
pci_write_config_byte(dev, priv->acpi.ctrl, reg_save | 0x10);
priv->acpi.save = reg_save;
}
static void lpc_ich_enable_gpio_space(struct pci_dev *dev)
{
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u8 reg_save;
pci_read_config_byte(dev, GPIOCTRL, &reg_save);
pci_write_config_byte(dev, GPIOCTRL, reg_save | 0x10);
lpc_ich_gpio_save = reg_save;
pci_read_config_byte(dev, priv->gpio.ctrl, &reg_save);
pci_write_config_byte(dev, priv->gpio.ctrl, reg_save | 0x10);
priv->gpio.save = reg_save;
}
static void lpc_ich_finalize_cell(struct mfd_cell *cell,
const struct pci_device_id *id)
static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell)
{
cell->platform_data = &lpc_chipset_info[id->driver_data];
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
cell->platform_data = &lpc_chipset_info[priv->chipset];
cell->pdata_size = sizeof(struct lpc_ich_info);
}
......@@ -721,9 +775,9 @@ static int lpc_ich_check_conflict_gpio(struct resource *res)
return use_gpio ? use_gpio : ret;
}
static int lpc_ich_init_gpio(struct pci_dev *dev,
const struct pci_device_id *id)
static int lpc_ich_init_gpio(struct pci_dev *dev)
{
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u32 base_addr_cfg;
u32 base_addr;
int ret;
......@@ -731,7 +785,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
struct resource *res;
/* Setup power management base register */
pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
......@@ -757,7 +811,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
gpe0_done:
/* Setup GPIO base register */
pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
pci_read_config_dword(dev, priv->gpio.base, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n");
......@@ -768,7 +822,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
/* Older devices provide fewer GPIO and have a smaller resource size. */
res = &gpio_ich_res[ICH_RES_GPIO];
res->start = base_addr;
switch (lpc_chipset_info[id->driver_data].gpio_version) {
switch (lpc_chipset_info[priv->chipset].gpio_version) {
case ICH_V5_GPIO:
case ICH_V10CORP_GPIO:
res->end = res->start + 128 - 1;
......@@ -784,10 +838,10 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
acpi_conflict = true;
goto gpio_done;
}
lpc_chipset_info[id->driver_data].use_gpio = ret;
lpc_chipset_info[priv->chipset].use_gpio = ret;
lpc_ich_enable_gpio_space(dev);
lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id);
lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_GPIO]);
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
1, NULL, 0, NULL);
......@@ -798,16 +852,16 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
return ret;
}
static int lpc_ich_init_wdt(struct pci_dev *dev,
const struct pci_device_id *id)
static int lpc_ich_init_wdt(struct pci_dev *dev)
{
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
u32 base_addr_cfg;
u32 base_addr;
int ret;
struct resource *res;
/* Setup power management base register */
pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
......@@ -830,7 +884,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev,
* we have to read RCBA from PCI Config space 0xf0 and use
* it as base. GCS = RCBA + ICH6_GCS(0x3410).
*/
if (lpc_chipset_info[id->driver_data].iTCO_version == 1) {
if (lpc_chipset_info[priv->chipset].iTCO_version == 1) {
/* Don't register iomem for TCO ver 1 */
lpc_ich_cells[LPC_WDT].num_resources--;
} else {
......@@ -847,7 +901,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev,
res->end = base_addr + ACPIBASE_GCS_END;
}
lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id);
lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
1, NULL, 0, NULL);
......@@ -858,14 +912,36 @@ static int lpc_ich_init_wdt(struct pci_dev *dev,
static int lpc_ich_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
struct lpc_ich_priv *priv;
int ret;
bool cell_added = false;
ret = lpc_ich_init_wdt(dev, id);
priv = devm_kzalloc(&dev->dev,
sizeof(struct lpc_ich_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->chipset = id->driver_data;
priv->acpi.save = -1;
priv->acpi.base = ACPIBASE;
priv->acpi.ctrl = ACPICTRL;
priv->gpio.save = -1;
if (priv->chipset <= LPC_ICH5) {
priv->gpio.base = GPIOBASE_ICH0;
priv->gpio.ctrl = GPIOCTRL_ICH0;
} else {
priv->gpio.base = GPIOBASE_ICH6;
priv->gpio.ctrl = GPIOCTRL_ICH6;
}
pci_set_drvdata(dev, priv);
ret = lpc_ich_init_wdt(dev);
if (!ret)
cell_added = true;
ret = lpc_ich_init_gpio(dev, id);
ret = lpc_ich_init_gpio(dev);
if (!ret)
cell_added = true;
......@@ -876,6 +952,7 @@ static int lpc_ich_probe(struct pci_dev *dev,
if (!cell_added) {
dev_warn(&dev->dev, "No MFD cells added\n");
lpc_ich_restore_config_space(dev);
pci_set_drvdata(dev, NULL);
return -ENODEV;
}
......@@ -886,6 +963,7 @@ static void lpc_ich_remove(struct pci_dev *dev)
{
mfd_remove_devices(&dev->dev);
lpc_ich_restore_config_space(dev);
pci_set_drvdata(dev, NULL);
}
static struct pci_driver lpc_ich_driver = {
......
......@@ -45,34 +45,32 @@ static struct resource smbus_sch_resource = {
.flags = IORESOURCE_IO,
};
static struct resource gpio_sch_resource = {
.flags = IORESOURCE_IO,
};
static struct mfd_cell lpc_sch_cells[] = {
{
.name = "isch_smbus",
.num_resources = 1,
.resources = &smbus_sch_resource,
},
{
.name = "sch_gpio",
.num_resources = 1,
.resources = &gpio_sch_resource,
},
};
static struct resource wdt_sch_resource = {
.flags = IORESOURCE_IO,
};
static struct mfd_cell tunnelcreek_cells[] = {
{
.name = "ie6xx_wdt",
.num_resources = 1,
.resources = &wdt_sch_resource,
},
static struct mfd_cell lpc_sch_cells[3];
static struct mfd_cell isch_smbus_cell = {
.name = "isch_smbus",
.num_resources = 1,
.resources = &smbus_sch_resource,
};
static struct mfd_cell sch_gpio_cell = {
.name = "sch_gpio",
.num_resources = 1,
.resources = &gpio_sch_resource,
};
static struct mfd_cell wdt_sch_cell = {
.name = "ie6xx_wdt",
.num_resources = 1,
.resources = &wdt_sch_resource,
};
static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = {
......@@ -88,79 +86,76 @@ static int lpc_sch_probe(struct pci_dev *dev,
{
unsigned int base_addr_cfg;
unsigned short base_addr;
int i;
int i, cells = 0;
int ret;
pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
if (!(base_addr_cfg & (1 << 31))) {
dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
return -ENODEV;
}
base_addr = (unsigned short)base_addr_cfg;
if (base_addr == 0) {
dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
return -ENODEV;
}
smbus_sch_resource.start = base_addr;
smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
base_addr = 0;
if (!(base_addr_cfg & (1 << 31)))
dev_warn(&dev->dev, "Decode of the SMBus I/O range disabled\n");
else
base_addr = (unsigned short)base_addr_cfg;
pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
if (!(base_addr_cfg & (1 << 31))) {
dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
return -ENODEV;
}
base_addr = (unsigned short)base_addr_cfg;
if (base_addr == 0) {
dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
return -ENODEV;
dev_warn(&dev->dev, "I/O space for SMBus uninitialized\n");
} else {
lpc_sch_cells[cells++] = isch_smbus_cell;
smbus_sch_resource.start = base_addr;
smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
}
gpio_sch_resource.start = base_addr;
if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB)
gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1;
pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
base_addr = 0;
if (!(base_addr_cfg & (1 << 31)))
dev_warn(&dev->dev, "Decode of the GPIO I/O range disabled\n");
else
gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
lpc_sch_cells[i].id = id->device;
base_addr = (unsigned short)base_addr_cfg;
ret = mfd_add_devices(&dev->dev, 0,
lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL,
0, NULL);
if (ret)
goto out_dev;
if (base_addr == 0) {
dev_warn(&dev->dev, "I/O space for GPIO uninitialized\n");
} else {
lpc_sch_cells[cells++] = sch_gpio_cell;
gpio_sch_resource.start = base_addr;
if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB)
gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1;
else
gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
}
if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC
|| id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) {
|| id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) {
pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
if (!(base_addr_cfg & (1 << 31))) {
dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
ret = -ENODEV;
goto out_dev;
base_addr = 0;
if (!(base_addr_cfg & (1 << 31)))
dev_warn(&dev->dev, "Decode of the WDT I/O range disabled\n");
else
base_addr = (unsigned short)base_addr_cfg;
if (base_addr == 0)
dev_warn(&dev->dev, "I/O space for WDT uninitialized\n");
else {
lpc_sch_cells[cells++] = wdt_sch_cell;
wdt_sch_resource.start = base_addr;
wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
}
base_addr = (unsigned short)base_addr_cfg;
if (base_addr == 0) {
dev_err(&dev->dev, "I/O space for WDT uninitialized\n");
ret = -ENODEV;
goto out_dev;
}
wdt_sch_resource.start = base_addr;
wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
}
for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++)
tunnelcreek_cells[i].id = id->device;
if (WARN_ON(cells > ARRAY_SIZE(lpc_sch_cells))) {
dev_err(&dev->dev, "Cell count exceeds array size");
return -ENODEV;
}
ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
ARRAY_SIZE(tunnelcreek_cells), NULL,
0, NULL);
if (cells == 0) {
dev_err(&dev->dev, "All decode registers disabled.\n");
return -ENODEV;
}
return ret;
out_dev:
mfd_remove_devices(&dev->dev);
for (i = 0; i < cells; i++)
lpc_sch_cells[i].id = id->device;
ret = mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL);
if (ret)
mfd_remove_devices(&dev->dev);
return ret;
}
......
......@@ -14,10 +14,13 @@
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/platform_device.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max8925.h>
#include <linux/of.h>
#include <linux/of_platform.h>
static struct resource bk_resources[] = {
{ 0x84, 0x84, "mode control", IORESOURCE_REG, },
......@@ -639,17 +642,33 @@ static struct irq_chip max8925_irq_chip = {
.irq_disable = max8925_irq_disable,
};
static int max8925_irq_domain_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw)
{
irq_set_chip_data(virq, d->host_data);
irq_set_chip_and_handler(virq, &max8925_irq_chip, handle_edge_irq);
irq_set_nested_thread(virq, 1);
#ifdef CONFIG_ARM
set_irq_flags(virq, IRQF_VALID);
#else
irq_set_noprobe(virq);
#endif
return 0;
}
static struct irq_domain_ops max8925_irq_domain_ops = {
.map = max8925_irq_domain_map,
.xlate = irq_domain_xlate_onetwocell,
};
static int max8925_irq_init(struct max8925_chip *chip, int irq,
struct max8925_platform_data *pdata)
{
unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
int i, ret;
int __irq;
int ret;
struct device_node *node = chip->dev->of_node;
if (!pdata || !pdata->irq_base) {
dev_warn(chip->dev, "No interrupt support on IRQ base\n");
return -EINVAL;
}
/* clear all interrupts */
max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ1);
max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ2);
......@@ -667,35 +686,30 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff);
mutex_init(&chip->irq_lock);
chip->core_irq = irq;
chip->irq_base = pdata->irq_base;
/* register with genirq */
for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
__irq = i + chip->irq_base;
irq_set_chip_data(__irq, chip);
irq_set_chip_and_handler(__irq, &max8925_irq_chip,
handle_edge_irq);
irq_set_nested_thread(__irq, 1);
#ifdef CONFIG_ARM
set_irq_flags(__irq, IRQF_VALID);
#else
irq_set_noprobe(__irq);
#endif
}
if (!irq) {
dev_warn(chip->dev, "No interrupt support on core IRQ\n");
goto tsc_irq;
chip->irq_base = irq_alloc_descs(-1, 0, MAX8925_NR_IRQS, 0);
if (chip->irq_base < 0) {
dev_err(chip->dev, "Failed to allocate interrupts, ret:%d\n",
chip->irq_base);
return -EBUSY;
}
irq_domain_add_legacy(node, MAX8925_NR_IRQS, chip->irq_base, 0,
&max8925_irq_domain_ops, chip);
/* request irq handler for pmic main irq*/
chip->core_irq = irq;
if (!chip->core_irq)
return -EBUSY;
ret = request_threaded_irq(irq, NULL, max8925_irq, flags | IRQF_ONESHOT,
"max8925", chip);
if (ret) {
dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret);
chip->core_irq = 0;
return -EBUSY;
}
tsc_irq:
/* request irq handler for pmic tsc irq*/
/* mask TSC interrupt */
max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f);
......@@ -704,7 +718,6 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
return 0;
}
chip->tsc_irq = pdata->tsc_irq;
ret = request_threaded_irq(chip->tsc_irq, NULL, max8925_tsc_irq,
flags | IRQF_ONESHOT, "max8925-tsc", chip);
if (ret) {
......@@ -846,7 +859,7 @@ int max8925_device_init(struct max8925_chip *chip,
ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
ARRAY_SIZE(rtc_devs),
&rtc_resources[0], chip->irq_base, NULL);
NULL, chip->irq_base, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add rtc subdev\n");
goto out;
......@@ -854,7 +867,7 @@ int max8925_device_init(struct max8925_chip *chip,
ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
ARRAY_SIZE(onkey_devs),
&onkey_resources[0], 0, NULL);
NULL, chip->irq_base, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add onkey subdev\n");
goto out_dev;
......@@ -873,21 +886,19 @@ int max8925_device_init(struct max8925_chip *chip,
goto out_dev;
}
if (pdata && pdata->power) {
ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
ARRAY_SIZE(power_devs),
&power_supply_resources[0], 0, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add power supply "
"subdev\n");
goto out_dev;
}
ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
ARRAY_SIZE(power_devs),
NULL, 0, NULL);
if (ret < 0) {
dev_err(chip->dev,
"Failed to add power supply subdev, err = %d\n", ret);
goto out_dev;
}
if (pdata && pdata->touch) {
ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
ARRAY_SIZE(touch_devs),
&touch_resources[0], 0, NULL);
NULL, chip->tsc_irq, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add touch subdev\n");
goto out_dev;
......
......@@ -135,13 +135,37 @@ static const struct i2c_device_id max8925_id_table[] = {
};
MODULE_DEVICE_TABLE(i2c, max8925_id_table);
static int max8925_dt_init(struct device_node *np, struct device *dev,
struct max8925_platform_data *pdata)
{
int ret;
ret = of_property_read_u32(np, "maxim,tsc-irq", &pdata->tsc_irq);
if (ret) {
dev_err(dev, "Not found maxim,tsc-irq property\n");
return -EINVAL;
}
return 0;
}
static int max8925_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max8925_platform_data *pdata = client->dev.platform_data;
static struct max8925_chip *chip;
if (!pdata) {
struct device_node *node = client->dev.of_node;
if (node && !pdata) {
/* parse DT to get platform data */
pdata = devm_kzalloc(&client->dev,
sizeof(struct max8925_platform_data),
GFP_KERNEL);
if (!pdata)
return -ENOMEM;
if (max8925_dt_init(node, &client->dev, pdata))
return -EINVAL;
} else if (!pdata) {
pr_info("%s: platform data is missing\n", __func__);
return -EINVAL;
}
......@@ -203,11 +227,18 @@ static int max8925_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(max8925_pm_ops, max8925_suspend, max8925_resume);
static const struct of_device_id max8925_dt_ids[] = {
{ .compatible = "maxim,max8925", },
{},
};
MODULE_DEVICE_TABLE(of, max8925_dt_ids);
static struct i2c_driver max8925_driver = {
.driver = {
.name = "max8925",
.owner = THIS_MODULE,
.pm = &max8925_pm_ops,
.of_match_table = of_match_ptr(max8925_dt_ids),
},
.probe = max8925_probe,
.remove = max8925_remove,
......@@ -217,7 +248,6 @@ static struct i2c_driver max8925_driver = {
static int __init max8925_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&max8925_driver);
if (ret != 0)
pr_err("Failed to register MAX8925 I2C driver: %d\n", ret);
......
......@@ -23,7 +23,6 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/platform_data/usb-omap.h>
......@@ -91,21 +90,23 @@
struct usbhs_hcd_omap {
int nports;
struct clk **utmi_clk;
struct clk **hsic60m_clk;
struct clk **hsic480m_clk;
struct clk *xclk60mhsp1_ck;
struct clk *xclk60mhsp2_ck;
struct clk *utmi_p1_fck;
struct clk *usbhost_p1_fck;
struct clk *utmi_p2_fck;
struct clk *usbhost_p2_fck;
struct clk *utmi_p1_gfclk;
struct clk *utmi_p2_gfclk;
struct clk *init_60m_fclk;
struct clk *ehci_logic_fck;
void __iomem *uhh_base;
struct usbhs_omap_platform_data platdata;
struct usbhs_omap_platform_data *pdata;
u32 usbhs_rev;
spinlock_t lock;
};
/*-------------------------------------------------------------------------*/
......@@ -184,19 +185,13 @@ static struct platform_device *omap_usbhs_alloc_child(const char *name,
static int omap_usbhs_alloc_children(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct usbhs_hcd_omap *omap;
struct ehci_hcd_omap_platform_data *ehci_data;
struct ohci_hcd_omap_platform_data *ohci_data;
struct usbhs_omap_platform_data *pdata = dev->platform_data;
struct platform_device *ehci;
struct platform_device *ohci;
struct resource *res;
struct resource resources[2];
int ret;
omap = platform_get_drvdata(pdev);
ehci_data = omap->platdata.ehci_data;
ohci_data = omap->platdata.ohci_data;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci");
if (!res) {
dev_err(dev, "EHCI get resource IORESOURCE_MEM failed\n");
......@@ -213,8 +208,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
}
resources[1] = *res;
ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, ehci_data,
sizeof(*ehci_data), dev);
ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, pdata,
sizeof(*pdata), dev);
if (!ehci) {
dev_err(dev, "omap_usbhs_alloc_child failed\n");
......@@ -238,8 +233,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
}
resources[1] = *res;
ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, ohci_data,
sizeof(*ohci_data), dev);
ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, pdata,
sizeof(*pdata), dev);
if (!ohci) {
dev_err(dev, "omap_usbhs_alloc_child failed\n");
ret = -ENOMEM;
......@@ -278,31 +273,52 @@ static bool is_ohci_port(enum usbhs_omap_port_mode pmode)
static int usbhs_runtime_resume(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = &omap->platdata;
unsigned long flags;
struct usbhs_omap_platform_data *pdata = omap->pdata;
int i, r;
dev_dbg(dev, "usbhs_runtime_resume\n");
if (!pdata) {
dev_dbg(dev, "missing platform_data\n");
return -ENODEV;
}
omap_tll_enable();
spin_lock_irqsave(&omap->lock, flags);
if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
if (!IS_ERR(omap->ehci_logic_fck))
clk_enable(omap->ehci_logic_fck);
if (is_ehci_tll_mode(pdata->port_mode[0]))
clk_enable(omap->usbhost_p1_fck);
if (is_ehci_tll_mode(pdata->port_mode[1]))
clk_enable(omap->usbhost_p2_fck);
clk_enable(omap->utmi_p1_fck);
clk_enable(omap->utmi_p2_fck);
for (i = 0; i < omap->nports; i++) {
switch (pdata->port_mode[i]) {
case OMAP_EHCI_PORT_MODE_HSIC:
if (!IS_ERR(omap->hsic60m_clk[i])) {
r = clk_enable(omap->hsic60m_clk[i]);
if (r) {
dev_err(dev,
"Can't enable port %d hsic60m clk:%d\n",
i, r);
}
}
spin_unlock_irqrestore(&omap->lock, flags);
if (!IS_ERR(omap->hsic480m_clk[i])) {
r = clk_enable(omap->hsic480m_clk[i]);
if (r) {
dev_err(dev,
"Can't enable port %d hsic480m clk:%d\n",
i, r);
}
}
/* Fall through as HSIC mode needs utmi_clk */
case OMAP_EHCI_PORT_MODE_TLL:
if (!IS_ERR(omap->utmi_clk[i])) {
r = clk_enable(omap->utmi_clk[i]);
if (r) {
dev_err(dev,
"Can't enable port %d clk : %d\n",
i, r);
}
}
break;
default:
break;
}
}
return 0;
}
......@@ -310,51 +326,122 @@ static int usbhs_runtime_resume(struct device *dev)
static int usbhs_runtime_suspend(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = &omap->platdata;
unsigned long flags;
struct usbhs_omap_platform_data *pdata = omap->pdata;
int i;
dev_dbg(dev, "usbhs_runtime_suspend\n");
if (!pdata) {
dev_dbg(dev, "missing platform_data\n");
return -ENODEV;
}
spin_lock_irqsave(&omap->lock, flags);
for (i = 0; i < omap->nports; i++) {
switch (pdata->port_mode[i]) {
case OMAP_EHCI_PORT_MODE_HSIC:
if (!IS_ERR(omap->hsic60m_clk[i]))
clk_disable(omap->hsic60m_clk[i]);
if (is_ehci_tll_mode(pdata->port_mode[0]))
clk_disable(omap->usbhost_p1_fck);
if (is_ehci_tll_mode(pdata->port_mode[1]))
clk_disable(omap->usbhost_p2_fck);
if (!IS_ERR(omap->hsic480m_clk[i]))
clk_disable(omap->hsic480m_clk[i]);
/* Fall through as utmi_clks were used in HSIC mode */
clk_disable(omap->utmi_p2_fck);
clk_disable(omap->utmi_p1_fck);
case OMAP_EHCI_PORT_MODE_TLL:
if (!IS_ERR(omap->utmi_clk[i]))
clk_disable(omap->utmi_clk[i]);
break;
default:
break;
}
}
if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
if (!IS_ERR(omap->ehci_logic_fck))
clk_disable(omap->ehci_logic_fck);
spin_unlock_irqrestore(&omap->lock, flags);
omap_tll_disable();
return 0;
}
static unsigned omap_usbhs_rev1_hostconfig(struct usbhs_hcd_omap *omap,
unsigned reg)
{
struct usbhs_omap_platform_data *pdata = omap->pdata;
int i;
for (i = 0; i < omap->nports; i++) {
switch (pdata->port_mode[i]) {
case OMAP_USBHS_PORT_MODE_UNUSED:
reg &= ~(OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS << i);
break;
case OMAP_EHCI_PORT_MODE_PHY:
if (pdata->single_ulpi_bypass)
break;
if (i == 0)
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
else
reg &= ~(OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS
<< (i-1));
break;
default:
if (pdata->single_ulpi_bypass)
break;
if (i == 0)
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
else
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS
<< (i-1);
break;
}
}
if (pdata->single_ulpi_bypass) {
/* bypass ULPI only if none of the ports use PHY mode */
reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
for (i = 0; i < omap->nports; i++) {
if (is_ehci_phy_mode(pdata->port_mode[i])) {
reg &= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
break;
}
}
}
return reg;
}
static unsigned omap_usbhs_rev2_hostconfig(struct usbhs_hcd_omap *omap,
unsigned reg)
{
struct usbhs_omap_platform_data *pdata = omap->pdata;
int i;
for (i = 0; i < omap->nports; i++) {
/* Clear port mode fields for PHY mode */
reg &= ~(OMAP4_P1_MODE_CLEAR << 2 * i);
if (is_ehci_tll_mode(pdata->port_mode[i]) ||
(is_ohci_port(pdata->port_mode[i])))
reg |= OMAP4_P1_MODE_TLL << 2 * i;
else if (is_ehci_hsic_mode(pdata->port_mode[i]))
reg |= OMAP4_P1_MODE_HSIC << 2 * i;
}
return reg;
}
static void omap_usbhs_init(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = &omap->platdata;
unsigned long flags;
struct usbhs_omap_platform_data *pdata = omap->pdata;
unsigned reg;
dev_dbg(dev, "starting TI HSUSB Controller\n");
if (pdata->ehci_data->phy_reset) {
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
gpio_request_one(pdata->ehci_data->reset_gpio_port[0],
if (pdata->phy_reset) {
if (gpio_is_valid(pdata->reset_gpio_port[0]))
gpio_request_one(pdata->reset_gpio_port[0],
GPIOF_OUT_INIT_LOW, "USB1 PHY reset");
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
gpio_request_one(pdata->ehci_data->reset_gpio_port[1],
if (gpio_is_valid(pdata->reset_gpio_port[1]))
gpio_request_one(pdata->reset_gpio_port[1],
GPIOF_OUT_INIT_LOW, "USB2 PHY reset");
/* Hold the PHY in RESET for enough time till DIR is high */
......@@ -362,9 +449,6 @@ static void omap_usbhs_init(struct device *dev)
}
pm_runtime_get_sync(dev);
spin_lock_irqsave(&omap->lock, flags);
omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev);
reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
/* setup ULPI bypass and burst configurations */
......@@ -374,89 +458,51 @@ static void omap_usbhs_init(struct device *dev)
reg |= OMAP4_UHH_HOSTCONFIG_APP_START_CLK;
reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
if (is_omap_usbhs_rev1(omap)) {
if (pdata->port_mode[0] == OMAP_USBHS_PORT_MODE_UNUSED)
reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
if (pdata->port_mode[1] == OMAP_USBHS_PORT_MODE_UNUSED)
reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
if (pdata->port_mode[2] == OMAP_USBHS_PORT_MODE_UNUSED)
reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
/* Bypass the TLL module for PHY mode operation */
if (pdata->single_ulpi_bypass) {
dev_dbg(dev, "OMAP3 ES version <= ES2.1\n");
if (is_ehci_phy_mode(pdata->port_mode[0]) ||
is_ehci_phy_mode(pdata->port_mode[1]) ||
is_ehci_phy_mode(pdata->port_mode[2]))
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
else
reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
} else {
dev_dbg(dev, "OMAP3 ES version > ES2.1\n");
if (is_ehci_phy_mode(pdata->port_mode[0]))
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
else
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
if (is_ehci_phy_mode(pdata->port_mode[1]))
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
else
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
if (is_ehci_phy_mode(pdata->port_mode[2]))
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
else
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
}
} else if (is_omap_usbhs_rev2(omap)) {
/* Clear port mode fields for PHY mode*/
reg &= ~OMAP4_P1_MODE_CLEAR;
reg &= ~OMAP4_P2_MODE_CLEAR;
switch (omap->usbhs_rev) {
case OMAP_USBHS_REV1:
omap_usbhs_rev1_hostconfig(omap, reg);
break;
if (is_ehci_tll_mode(pdata->port_mode[0]) ||
(is_ohci_port(pdata->port_mode[0])))
reg |= OMAP4_P1_MODE_TLL;
else if (is_ehci_hsic_mode(pdata->port_mode[0]))
reg |= OMAP4_P1_MODE_HSIC;
case OMAP_USBHS_REV2:
omap_usbhs_rev2_hostconfig(omap, reg);
break;
if (is_ehci_tll_mode(pdata->port_mode[1]) ||
(is_ohci_port(pdata->port_mode[1])))
reg |= OMAP4_P2_MODE_TLL;
else if (is_ehci_hsic_mode(pdata->port_mode[1]))
reg |= OMAP4_P2_MODE_HSIC;
default: /* newer revisions */
omap_usbhs_rev2_hostconfig(omap, reg);
break;
}
usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
spin_unlock_irqrestore(&omap->lock, flags);
pm_runtime_put_sync(dev);
if (pdata->ehci_data->phy_reset) {
if (pdata->phy_reset) {
/* Hold the PHY in RESET for enough time till
* PHY is settled and ready
*/
udelay(10);
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
if (gpio_is_valid(pdata->reset_gpio_port[0]))
gpio_set_value_cansleep
(pdata->ehci_data->reset_gpio_port[0], 1);
(pdata->reset_gpio_port[0], 1);
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
if (gpio_is_valid(pdata->reset_gpio_port[1]))
gpio_set_value_cansleep
(pdata->ehci_data->reset_gpio_port[1], 1);
(pdata->reset_gpio_port[1], 1);
}
}
static void omap_usbhs_deinit(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = &omap->platdata;
struct usbhs_omap_platform_data *pdata = omap->pdata;
if (pdata->ehci_data->phy_reset) {
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
gpio_free(pdata->ehci_data->reset_gpio_port[0]);
if (pdata->phy_reset) {
if (gpio_is_valid(pdata->reset_gpio_port[0]))
gpio_free(pdata->reset_gpio_port[0]);
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
gpio_free(pdata->ehci_data->reset_gpio_port[1]);
if (gpio_is_valid(pdata->reset_gpio_port[1]))
gpio_free(pdata->reset_gpio_port[1]);
}
}
......@@ -474,137 +520,185 @@ static int usbhs_omap_probe(struct platform_device *pdev)
struct resource *res;
int ret = 0;
int i;
bool need_logic_fck;
if (!pdata) {
dev_err(dev, "Missing platform data\n");
ret = -ENOMEM;
goto end_probe;
return -ENODEV;
}
omap = kzalloc(sizeof(*omap), GFP_KERNEL);
omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
if (!omap) {
dev_err(dev, "Memory allocation failed\n");
ret = -ENOMEM;
goto end_probe;
return -ENOMEM;
}
spin_lock_init(&omap->lock);
for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
omap->platdata.port_mode[i] = pdata->port_mode[i];
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh");
omap->uhh_base = devm_request_and_ioremap(dev, res);
if (!omap->uhh_base) {
dev_err(dev, "Resource request/ioremap failed\n");
return -EADDRNOTAVAIL;
}
omap->platdata.ehci_data = pdata->ehci_data;
omap->platdata.ohci_data = pdata->ohci_data;
omap->pdata = pdata;
pm_runtime_enable(dev);
platform_set_drvdata(pdev, omap);
pm_runtime_get_sync(dev);
omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
is_ehci_hsic_mode(i)) {
omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
if (IS_ERR(omap->ehci_logic_fck)) {
ret = PTR_ERR(omap->ehci_logic_fck);
dev_warn(dev, "ehci_logic_fck failed:%d\n",
ret);
}
/* we need to call runtime suspend before we update omap->nports
* to prevent unbalanced clk_disable()
*/
pm_runtime_put_sync(dev);
/*
* If platform data contains nports then use that
* else make out number of ports from USBHS revision
*/
if (pdata->nports) {
omap->nports = pdata->nports;
} else {
switch (omap->usbhs_rev) {
case OMAP_USBHS_REV1:
omap->nports = 3;
break;
case OMAP_USBHS_REV2:
omap->nports = 2;
break;
default:
omap->nports = OMAP3_HS_USB_PORTS;
dev_dbg(dev,
"USB HOST Rev:0x%d not recognized, assuming %d ports\n",
omap->usbhs_rev, omap->nports);
break;
}
}
omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
if (IS_ERR(omap->utmi_p1_fck)) {
ret = PTR_ERR(omap->utmi_p1_fck);
dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
goto err_end;
i = sizeof(struct clk *) * omap->nports;
omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL);
omap->hsic480m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
omap->hsic60m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
if (!omap->utmi_clk || !omap->hsic480m_clk || !omap->hsic60m_clk) {
dev_err(dev, "Memory allocation failed\n");
ret = -ENOMEM;
goto err_mem;
}
need_logic_fck = false;
for (i = 0; i < omap->nports; i++) {
if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
is_ehci_hsic_mode(i))
need_logic_fck |= true;
}
omap->ehci_logic_fck = ERR_PTR(-EINVAL);
if (need_logic_fck) {
omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
if (IS_ERR(omap->ehci_logic_fck)) {
ret = PTR_ERR(omap->ehci_logic_fck);
dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret);
}
}
omap->utmi_p1_gfclk = clk_get(dev, "utmi_p1_gfclk");
if (IS_ERR(omap->utmi_p1_gfclk)) {
ret = PTR_ERR(omap->utmi_p1_gfclk);
dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
goto err_p1_gfclk;
}
omap->utmi_p2_gfclk = clk_get(dev, "utmi_p2_gfclk");
if (IS_ERR(omap->utmi_p2_gfclk)) {
ret = PTR_ERR(omap->utmi_p2_gfclk);
dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
goto err_p2_gfclk;
}
omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
if (IS_ERR(omap->xclk60mhsp1_ck)) {
ret = PTR_ERR(omap->xclk60mhsp1_ck);
dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
goto err_utmi_p1_fck;
}
omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk");
if (IS_ERR(omap->utmi_p2_fck)) {
ret = PTR_ERR(omap->utmi_p2_fck);
dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
goto err_xclk60mhsp1_ck;
goto err_xclk60mhsp1;
}
omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
if (IS_ERR(omap->xclk60mhsp2_ck)) {
ret = PTR_ERR(omap->xclk60mhsp2_ck);
dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
goto err_utmi_p2_fck;
}
omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk");
if (IS_ERR(omap->usbhost_p1_fck)) {
ret = PTR_ERR(omap->usbhost_p1_fck);
dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret);
goto err_xclk60mhsp2_ck;
}
omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk");
if (IS_ERR(omap->usbhost_p2_fck)) {
ret = PTR_ERR(omap->usbhost_p2_fck);
dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret);
goto err_usbhost_p1_fck;
goto err_xclk60mhsp2;
}
omap->init_60m_fclk = clk_get(dev, "init_60m_fclk");
if (IS_ERR(omap->init_60m_fclk)) {
ret = PTR_ERR(omap->init_60m_fclk);
dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
goto err_usbhost_p2_fck;
goto err_init60m;
}
for (i = 0; i < omap->nports; i++) {
char clkname[30];
/* clock names are indexed from 1*/
snprintf(clkname, sizeof(clkname),
"usb_host_hs_utmi_p%d_clk", i + 1);
/* If a clock is not found we won't bail out as not all
* platforms have all clocks and we can function without
* them
*/
omap->utmi_clk[i] = clk_get(dev, clkname);
if (IS_ERR(omap->utmi_clk[i]))
dev_dbg(dev, "Failed to get clock : %s : %ld\n",
clkname, PTR_ERR(omap->utmi_clk[i]));
snprintf(clkname, sizeof(clkname),
"usb_host_hs_hsic480m_p%d_clk", i + 1);
omap->hsic480m_clk[i] = clk_get(dev, clkname);
if (IS_ERR(omap->hsic480m_clk[i]))
dev_dbg(dev, "Failed to get clock : %s : %ld\n",
clkname, PTR_ERR(omap->hsic480m_clk[i]));
snprintf(clkname, sizeof(clkname),
"usb_host_hs_hsic60m_p%d_clk", i + 1);
omap->hsic60m_clk[i] = clk_get(dev, clkname);
if (IS_ERR(omap->hsic60m_clk[i]))
dev_dbg(dev, "Failed to get clock : %s : %ld\n",
clkname, PTR_ERR(omap->hsic60m_clk[i]));
}
if (is_ehci_phy_mode(pdata->port_mode[0])) {
/* for OMAP3 , the clk set paretn fails */
ret = clk_set_parent(omap->utmi_p1_fck,
/* for OMAP3, clk_set_parent fails */
ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->xclk60mhsp1_ck);
if (ret != 0)
dev_err(dev, "xclk60mhsp1_ck set parent"
"failed error:%d\n", ret);
dev_dbg(dev, "xclk60mhsp1_ck set parent failed: %d\n",
ret);
} else if (is_ehci_tll_mode(pdata->port_mode[0])) {
ret = clk_set_parent(omap->utmi_p1_fck,
ret = clk_set_parent(omap->utmi_p1_gfclk,
omap->init_60m_fclk);
if (ret != 0)
dev_err(dev, "init_60m_fclk set parent"
"failed error:%d\n", ret);
dev_dbg(dev, "P0 init_60m_fclk set parent failed: %d\n",
ret);
}
if (is_ehci_phy_mode(pdata->port_mode[1])) {
ret = clk_set_parent(omap->utmi_p2_fck,
ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->xclk60mhsp2_ck);
if (ret != 0)
dev_err(dev, "xclk60mhsp2_ck set parent"
"failed error:%d\n", ret);
dev_dbg(dev, "xclk60mhsp2_ck set parent failed: %d\n",
ret);
} else if (is_ehci_tll_mode(pdata->port_mode[1])) {
ret = clk_set_parent(omap->utmi_p2_fck,
ret = clk_set_parent(omap->utmi_p2_gfclk,
omap->init_60m_fclk);
if (ret != 0)
dev_err(dev, "init_60m_fclk set parent"
"failed error:%d\n", ret);
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh");
if (!res) {
dev_err(dev, "UHH EHCI get resource failed\n");
ret = -ENODEV;
goto err_init_60m_fclk;
}
omap->uhh_base = ioremap(res->start, resource_size(res));
if (!omap->uhh_base) {
dev_err(dev, "UHH ioremap failed\n");
ret = -ENOMEM;
goto err_init_60m_fclk;
dev_dbg(dev, "P1 init_60m_fclk set parent failed: %d\n",
ret);
}
platform_set_drvdata(pdev, omap);
omap_usbhs_init(dev);
ret = omap_usbhs_alloc_children(pdev);
if (ret) {
......@@ -612,39 +706,41 @@ static int usbhs_omap_probe(struct platform_device *pdev)
goto err_alloc;
}
goto end_probe;
return 0;
err_alloc:
omap_usbhs_deinit(&pdev->dev);
iounmap(omap->uhh_base);
err_init_60m_fclk:
clk_put(omap->init_60m_fclk);
err_usbhost_p2_fck:
clk_put(omap->usbhost_p2_fck);
for (i = 0; i < omap->nports; i++) {
if (!IS_ERR(omap->utmi_clk[i]))
clk_put(omap->utmi_clk[i]);
if (!IS_ERR(omap->hsic60m_clk[i]))
clk_put(omap->hsic60m_clk[i]);
if (!IS_ERR(omap->hsic480m_clk[i]))
clk_put(omap->hsic480m_clk[i]);
}
err_usbhost_p1_fck:
clk_put(omap->usbhost_p1_fck);
clk_put(omap->init_60m_fclk);
err_xclk60mhsp2_ck:
err_init60m:
clk_put(omap->xclk60mhsp2_ck);
err_utmi_p2_fck:
clk_put(omap->utmi_p2_fck);
err_xclk60mhsp1_ck:
err_xclk60mhsp2:
clk_put(omap->xclk60mhsp1_ck);
err_utmi_p1_fck:
clk_put(omap->utmi_p1_fck);
err_xclk60mhsp1:
clk_put(omap->utmi_p2_gfclk);
err_end:
clk_put(omap->ehci_logic_fck);
err_p2_gfclk:
clk_put(omap->utmi_p1_gfclk);
err_p1_gfclk:
if (!IS_ERR(omap->ehci_logic_fck))
clk_put(omap->ehci_logic_fck);
err_mem:
pm_runtime_disable(dev);
kfree(omap);
end_probe:
return ret;
}
......@@ -657,19 +753,29 @@ static int usbhs_omap_probe(struct platform_device *pdev)
static int usbhs_omap_remove(struct platform_device *pdev)
{
struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev);
int i;
omap_usbhs_deinit(&pdev->dev);
iounmap(omap->uhh_base);
for (i = 0; i < omap->nports; i++) {
if (!IS_ERR(omap->utmi_clk[i]))
clk_put(omap->utmi_clk[i]);
if (!IS_ERR(omap->hsic60m_clk[i]))
clk_put(omap->hsic60m_clk[i]);
if (!IS_ERR(omap->hsic480m_clk[i]))
clk_put(omap->hsic480m_clk[i]);
}
clk_put(omap->init_60m_fclk);
clk_put(omap->usbhost_p2_fck);
clk_put(omap->usbhost_p1_fck);
clk_put(omap->utmi_p1_gfclk);
clk_put(omap->utmi_p2_gfclk);
clk_put(omap->xclk60mhsp2_ck);
clk_put(omap->utmi_p2_fck);
clk_put(omap->xclk60mhsp1_ck);
clk_put(omap->utmi_p1_fck);
clk_put(omap->ehci_logic_fck);
if (!IS_ERR(omap->ehci_logic_fck))
clk_put(omap->ehci_logic_fck);
pm_runtime_disable(&pdev->dev);
kfree(omap);
return 0;
}
......@@ -685,7 +791,7 @@ static struct platform_driver usbhs_omap_driver = {
.owner = THIS_MODULE,
.pm = &usbhsomap_dev_pm_ops,
},
.remove = __exit_p(usbhs_omap_remove),
.remove = usbhs_omap_remove,
};
MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>");
......
......@@ -54,10 +54,13 @@
#define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num)
#define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT 24
#define OMAP_TLL_CHANNEL_CONF_DRVVBUS (1 << 16)
#define OMAP_TLL_CHANNEL_CONF_CHRGVBUS (1 << 15)
#define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11)
#define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10)
#define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9)
#define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8)
#define OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI (2 << 1)
#define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS (1 << 1)
#define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0)
......@@ -92,21 +95,25 @@
#define OMAP_USBTLL_REV1 0x00000015 /* OMAP3 */
#define OMAP_USBTLL_REV2 0x00000018 /* OMAP 3630 */
#define OMAP_USBTLL_REV3 0x00000004 /* OMAP4 */
#define OMAP_USBTLL_REV4 0x00000006 /* OMAP5 */
#define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL)
/* only PHY and UNUSED modes don't need TLL */
#define omap_usb_mode_needs_tll(x) ((x) != OMAP_USBHS_PORT_MODE_UNUSED &&\
(x) != OMAP_EHCI_PORT_MODE_PHY)
struct usbtll_omap {
struct clk *usbtll_p1_fck;
struct clk *usbtll_p2_fck;
struct usbtll_omap_platform_data platdata;
/* secure the register updates */
spinlock_t lock;
int nch; /* num. of channels */
struct usbhs_omap_platform_data *pdata;
struct clk **ch_clk;
};
/*-------------------------------------------------------------------------*/
const char usbtll_driver_name[] = USBTLL_DRIVER_NAME;
struct platform_device *tll_pdev;
static const char usbtll_driver_name[] = USBTLL_DRIVER_NAME;
static struct device *tll_dev;
static DEFINE_SPINLOCK(tll_lock); /* serialize access to tll_dev */
/*-------------------------------------------------------------------------*/
......@@ -203,84 +210,84 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode)
static int usbtll_omap_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct usbtll_omap_platform_data *pdata = dev->platform_data;
struct usbhs_omap_platform_data *pdata = dev->platform_data;
void __iomem *base;
struct resource *res;
struct usbtll_omap *tll;
unsigned reg;
unsigned long flags;
int ret = 0;
int i, ver, count;
int i, ver;
bool needs_tll;
dev_dbg(dev, "starting TI HSUSB TLL Controller\n");
tll = kzalloc(sizeof(struct usbtll_omap), GFP_KERNEL);
tll = devm_kzalloc(dev, sizeof(struct usbtll_omap), GFP_KERNEL);
if (!tll) {
dev_err(dev, "Memory allocation failed\n");
ret = -ENOMEM;
goto end;
return -ENOMEM;
}
spin_lock_init(&tll->lock);
for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
tll->platdata.port_mode[i] = pdata->port_mode[i];
tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk");
if (IS_ERR(tll->usbtll_p1_fck)) {
ret = PTR_ERR(tll->usbtll_p1_fck);
dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret);
goto err_tll;
if (!pdata) {
dev_err(dev, "Platform data missing\n");
return -ENODEV;
}
tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk");
if (IS_ERR(tll->usbtll_p2_fck)) {
ret = PTR_ERR(tll->usbtll_p2_fck);
dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret);
goto err_usbtll_p1_fck;
}
tll->pdata = pdata;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "usb tll get resource failed\n");
ret = -ENODEV;
goto err_usbtll_p2_fck;
}
base = ioremap(res->start, resource_size(res));
base = devm_request_and_ioremap(dev, res);
if (!base) {
dev_err(dev, "TLL ioremap failed\n");
ret = -ENOMEM;
goto err_usbtll_p2_fck;
ret = -EADDRNOTAVAIL;
dev_err(dev, "Resource request/ioremap failed:%d\n", ret);
return ret;
}
platform_set_drvdata(pdev, tll);
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
spin_lock_irqsave(&tll->lock, flags);
ver = usbtll_read(base, OMAP_USBTLL_REVISION);
switch (ver) {
case OMAP_USBTLL_REV1:
case OMAP_USBTLL_REV2:
count = OMAP_TLL_CHANNEL_COUNT;
case OMAP_USBTLL_REV4:
tll->nch = OMAP_TLL_CHANNEL_COUNT;
break;
case OMAP_USBTLL_REV2:
case OMAP_USBTLL_REV3:
count = OMAP_REV2_TLL_CHANNEL_COUNT;
tll->nch = OMAP_REV2_TLL_CHANNEL_COUNT;
break;
default:
dev_err(dev, "TLL version failed\n");
ret = -ENODEV;
goto err_ioremap;
tll->nch = OMAP_TLL_CHANNEL_COUNT;
dev_dbg(dev,
"USB TLL Rev : 0x%x not recognized, assuming %d channels\n",
ver, tll->nch);
break;
}
if (is_ehci_tll_mode(pdata->port_mode[0]) ||
is_ehci_tll_mode(pdata->port_mode[1]) ||
is_ehci_tll_mode(pdata->port_mode[2]) ||
is_ohci_port(pdata->port_mode[0]) ||
is_ohci_port(pdata->port_mode[1]) ||
is_ohci_port(pdata->port_mode[2])) {
tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]),
GFP_KERNEL);
if (!tll->ch_clk) {
ret = -ENOMEM;
dev_err(dev, "Couldn't allocate memory for channel clocks\n");
goto err_clk_alloc;
}
for (i = 0; i < tll->nch; i++) {
char clkname[] = "usb_tll_hs_usb_chx_clk";
snprintf(clkname, sizeof(clkname),
"usb_tll_hs_usb_ch%d_clk", i);
tll->ch_clk[i] = clk_get(dev, clkname);
if (IS_ERR(tll->ch_clk[i]))
dev_dbg(dev, "can't get clock : %s\n", clkname);
}
needs_tll = false;
for (i = 0; i < tll->nch; i++)
needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]);
if (needs_tll) {
/* Program Common TLL register */
reg = usbtll_read(base, OMAP_TLL_SHARED_CONF);
......@@ -292,7 +299,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
usbtll_write(base, OMAP_TLL_SHARED_CONF, reg);
/* Enable channels now */
for (i = 0; i < count; i++) {
for (i = 0; i < tll->nch; i++) {
reg = usbtll_read(base, OMAP_TLL_CHANNEL_CONF(i));
if (is_ohci_port(pdata->port_mode[i])) {
......@@ -308,6 +315,15 @@ static int usbtll_omap_probe(struct platform_device *pdev)
reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE
| OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
| OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE);
} else if (pdata->port_mode[i] ==
OMAP_EHCI_PORT_MODE_HSIC) {
/*
* HSIC Mode requires UTMI port configurations
*/
reg |= OMAP_TLL_CHANNEL_CONF_DRVVBUS
| OMAP_TLL_CHANNEL_CONF_CHRGVBUS
| OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI
| OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF;
} else {
continue;
}
......@@ -320,25 +336,18 @@ static int usbtll_omap_probe(struct platform_device *pdev)
}
}
err_ioremap:
spin_unlock_irqrestore(&tll->lock, flags);
iounmap(base);
pm_runtime_put_sync(dev);
tll_pdev = pdev;
if (!ret)
goto end;
pm_runtime_disable(dev);
/* only after this can omap_tll_enable/disable work */
spin_lock(&tll_lock);
tll_dev = dev;
spin_unlock(&tll_lock);
err_usbtll_p2_fck:
clk_put(tll->usbtll_p2_fck);
err_usbtll_p1_fck:
clk_put(tll->usbtll_p1_fck);
return 0;
err_tll:
kfree(tll);
err_clk_alloc:
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
end:
return ret;
}
......@@ -351,36 +360,42 @@ static int usbtll_omap_probe(struct platform_device *pdev)
static int usbtll_omap_remove(struct platform_device *pdev)
{
struct usbtll_omap *tll = platform_get_drvdata(pdev);
int i;
spin_lock(&tll_lock);
tll_dev = NULL;
spin_unlock(&tll_lock);
for (i = 0; i < tll->nch; i++)
if (!IS_ERR(tll->ch_clk[i]))
clk_put(tll->ch_clk[i]);
clk_put(tll->usbtll_p2_fck);
clk_put(tll->usbtll_p1_fck);
pm_runtime_disable(&pdev->dev);
kfree(tll);
return 0;
}
static int usbtll_runtime_resume(struct device *dev)
{
struct usbtll_omap *tll = dev_get_drvdata(dev);
struct usbtll_omap_platform_data *pdata = &tll->platdata;
unsigned long flags;
struct usbhs_omap_platform_data *pdata = tll->pdata;
int i;
dev_dbg(dev, "usbtll_runtime_resume\n");
if (!pdata) {
dev_dbg(dev, "missing platform_data\n");
return -ENODEV;
}
spin_lock_irqsave(&tll->lock, flags);
for (i = 0; i < tll->nch; i++) {
if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
int r;
if (is_ehci_tll_mode(pdata->port_mode[0]))
clk_enable(tll->usbtll_p1_fck);
if (is_ehci_tll_mode(pdata->port_mode[1]))
clk_enable(tll->usbtll_p2_fck);
if (IS_ERR(tll->ch_clk[i]))
continue;
spin_unlock_irqrestore(&tll->lock, flags);
r = clk_enable(tll->ch_clk[i]);
if (r) {
dev_err(dev,
"Error enabling ch %d clock: %d\n", i, r);
}
}
}
return 0;
}
......@@ -388,26 +403,18 @@ static int usbtll_runtime_resume(struct device *dev)
static int usbtll_runtime_suspend(struct device *dev)
{
struct usbtll_omap *tll = dev_get_drvdata(dev);
struct usbtll_omap_platform_data *pdata = &tll->platdata;
unsigned long flags;
struct usbhs_omap_platform_data *pdata = tll->pdata;
int i;
dev_dbg(dev, "usbtll_runtime_suspend\n");
if (!pdata) {
dev_dbg(dev, "missing platform_data\n");
return -ENODEV;
for (i = 0; i < tll->nch; i++) {
if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
if (!IS_ERR(tll->ch_clk[i]))
clk_disable(tll->ch_clk[i]);
}
}
spin_lock_irqsave(&tll->lock, flags);
if (is_ehci_tll_mode(pdata->port_mode[0]))
clk_disable(tll->usbtll_p1_fck);
if (is_ehci_tll_mode(pdata->port_mode[1]))
clk_disable(tll->usbtll_p2_fck);
spin_unlock_irqrestore(&tll->lock, flags);
return 0;
}
......@@ -429,21 +436,39 @@ static struct platform_driver usbtll_omap_driver = {
int omap_tll_enable(void)
{
if (!tll_pdev) {
pr_err("missing omap usbhs tll platform_data\n");
return -ENODEV;
int ret;
spin_lock(&tll_lock);
if (!tll_dev) {
pr_err("%s: OMAP USB TLL not initialized\n", __func__);
ret = -ENODEV;
} else {
ret = pm_runtime_get_sync(tll_dev);
}
return pm_runtime_get_sync(&tll_pdev->dev);
spin_unlock(&tll_lock);
return ret;
}
EXPORT_SYMBOL_GPL(omap_tll_enable);
int omap_tll_disable(void)
{
if (!tll_pdev) {
pr_err("missing omap usbhs tll platform_data\n");
return -ENODEV;
int ret;
spin_lock(&tll_lock);
if (!tll_dev) {
pr_err("%s: OMAP USB TLL not initialized\n", __func__);
ret = -ENODEV;
} else {
ret = pm_runtime_put_sync(tll_dev);
}
return pm_runtime_put_sync(&tll_pdev->dev);
spin_unlock(&tll_lock);
return ret;
}
EXPORT_SYMBOL_GPL(omap_tll_disable);
......
......@@ -39,6 +39,14 @@ enum palmas_ids {
PALMAS_USB_ID,
};
static struct resource palmas_rtc_resources[] = {
{
.start = PALMAS_RTC_ALARM_IRQ,
.end = PALMAS_RTC_ALARM_IRQ,
.flags = IORESOURCE_IRQ,
},
};
static const struct mfd_cell palmas_children[] = {
{
.name = "palmas-pmic",
......@@ -59,6 +67,8 @@ static const struct mfd_cell palmas_children[] = {
{
.name = "palmas-rtc",
.id = PALMAS_RTC_ID,
.resources = &palmas_rtc_resources[0],
.num_resources = ARRAY_SIZE(palmas_rtc_resources),
},
{
.name = "palmas-pwrbutton",
......@@ -456,8 +466,8 @@ static int palmas_i2c_probe(struct i2c_client *i2c,
ret = mfd_add_devices(palmas->dev, -1,
children, ARRAY_SIZE(palmas_children),
NULL, regmap_irq_chip_get_base(palmas->irq_data),
NULL);
NULL, 0,
regmap_irq_get_domain(palmas->irq_data));
kfree(children);
if (ret < 0)
......
......@@ -115,14 +115,24 @@ static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card)
static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
{
u8 mask, val;
int err;
mask = (BPP_REG_TUNED18 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_MASK;
if (voltage == OUTPUT_3V3)
if (voltage == OUTPUT_3V3) {
err = rtsx_pci_write_register(pcr,
SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D);
if (err < 0)
return err;
val = (BPP_ASIC_3V3 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_3V3;
else if (voltage == OUTPUT_1V8)
} else if (voltage == OUTPUT_1V8) {
err = rtsx_pci_write_register(pcr,
SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B);
if (err < 0)
return err;
val = (BPP_ASIC_1V8 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_1V8;
else
} else {
return -EINVAL;
}
return rtsx_pci_write_register(pcr, LDO_CTL, mask, val);
}
......
......@@ -149,10 +149,18 @@ static int rts5209_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
int err;
if (voltage == OUTPUT_3V3) {
err = rtsx_pci_write_register(pcr,
SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D);
if (err < 0)
return err;
err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
if (err < 0)
return err;
} else if (voltage == OUTPUT_1V8) {
err = rtsx_pci_write_register(pcr,
SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B);
if (err < 0)
return err;
err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24);
if (err < 0)
return err;
......
/* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author:
* Wei WANG <wei_wang@realsil.com.cn>
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*
* Roger Tseng <rogerable@realtek.com>
* No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mfd/rtsx_pci.h>
#include "rtsx_pcr.h"
static int rts5227_extra_init_hw(struct rtsx_pcr *pcr)
{
u16 cap;
rtsx_pci_init_cmd(pcr);
/* Configure GPIO as output */
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02);
/* Switch LDO3318 source from DV33 to card_3v3 */
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01);
/* LED shine disabled, set initial shine cycle period */
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02);
/* Configure LTR */
pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &cap);
if (cap & PCI_EXP_LTR_EN)
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LTR_CTL, 0xFF, 0xA3);
/* Configure OBFF */
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OBFF_CFG, 0x03, 0x03);
/* Configure force_clock_req
* Maybe We should define 0xFF03 as some name
*/
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, 0xFF03, 0x08, 0x08);
/* Correct driving */
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
SD30_CLK_DRIVE_SEL, 0xFF, 0x96);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
SD30_CMD_DRIVE_SEL, 0xFF, 0x96);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
SD30_DAT_DRIVE_SEL, 0xFF, 0x96);
return rtsx_pci_send_cmd(pcr, 100);
}
static int rts5227_optimize_phy(struct rtsx_pcr *pcr)
{
/* Optimize RX sensitivity */
return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42);
}
static int rts5227_turn_on_led(struct rtsx_pcr *pcr)
{
return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02);
}
static int rts5227_turn_off_led(struct rtsx_pcr *pcr)
{
return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00);
}
static int rts5227_enable_auto_blink(struct rtsx_pcr *pcr)
{
return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08);
}
static int rts5227_disable_auto_blink(struct rtsx_pcr *pcr)
{
return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00);
}
static int rts5227_card_power_on(struct rtsx_pcr *pcr, int card)
{
int err;
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
SD_POWER_MASK, SD_PARTIAL_POWER_ON);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
LDO3318_PWR_MASK, 0x02);
err = rtsx_pci_send_cmd(pcr, 100);
if (err < 0)
return err;
/* To avoid too large in-rush current */
udelay(150);
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
SD_POWER_MASK, SD_POWER_ON);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
LDO3318_PWR_MASK, 0x06);
err = rtsx_pci_send_cmd(pcr, 100);
if (err < 0)
return err;
return 0;
}
static int rts5227_card_power_off(struct rtsx_pcr *pcr, int card)
{
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
SD_POWER_MASK | PMOS_STRG_MASK,
SD_POWER_OFF | PMOS_STRG_400mA);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
LDO3318_PWR_MASK, 0X00);
return rtsx_pci_send_cmd(pcr, 100);
}
static int rts5227_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
{
int err;
u8 drive_sel;
if (voltage == OUTPUT_3V3) {
err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
if (err < 0)
return err;
drive_sel = 0x96;
} else if (voltage == OUTPUT_1V8) {
err = rtsx_pci_write_phy_register(pcr, 0x11, 0x3C02);
if (err < 0)
return err;
err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C80 | 0x24);
if (err < 0)
return err;
drive_sel = 0xB3;
} else {
return -EINVAL;
}
/* set pad drive */
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL,
0xFF, drive_sel);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL,
0xFF, drive_sel);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL,
0xFF, drive_sel);
return rtsx_pci_send_cmd(pcr, 100);
}
static const struct pcr_ops rts5227_pcr_ops = {
.extra_init_hw = rts5227_extra_init_hw,
.optimize_phy = rts5227_optimize_phy,
.turn_on_led = rts5227_turn_on_led,
.turn_off_led = rts5227_turn_off_led,
.enable_auto_blink = rts5227_enable_auto_blink,
.disable_auto_blink = rts5227_disable_auto_blink,
.card_power_on = rts5227_card_power_on,
.card_power_off = rts5227_card_power_off,
.switch_output_voltage = rts5227_switch_output_voltage,
.cd_deglitch = NULL,
.conv_clk_and_div_n = NULL,
};
/* SD Pull Control Enable:
* SD_DAT[3:0] ==> pull up
* SD_CD ==> pull up
* SD_WP ==> pull up
* SD_CMD ==> pull up
* SD_CLK ==> pull down
*/
static const u32 rts5227_sd_pull_ctl_enable_tbl[] = {
RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9),
0,
};
/* SD Pull Control Disable:
* SD_DAT[3:0] ==> pull down
* SD_CD ==> pull up
* SD_WP ==> pull down
* SD_CMD ==> pull down
* SD_CLK ==> pull down
*/
static const u32 rts5227_sd_pull_ctl_disable_tbl[] = {
RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5),
0,
};
/* MS Pull Control Enable:
* MS CD ==> pull up
* others ==> pull down
*/
static const u32 rts5227_ms_pull_ctl_enable_tbl[] = {
RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
0,
};
/* MS Pull Control Disable:
* MS CD ==> pull up
* others ==> pull down
*/
static const u32 rts5227_ms_pull_ctl_disable_tbl[] = {
RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
0,
};
void rts5227_init_params(struct rtsx_pcr *pcr)
{
pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;
pcr->num_slots = 2;
pcr->ops = &rts5227_pcr_ops;
pcr->sd_pull_ctl_enable_tbl = rts5227_sd_pull_ctl_enable_tbl;
pcr->sd_pull_ctl_disable_tbl = rts5227_sd_pull_ctl_disable_tbl;
pcr->ms_pull_ctl_enable_tbl = rts5227_ms_pull_ctl_enable_tbl;
pcr->ms_pull_ctl_disable_tbl = rts5227_ms_pull_ctl_disable_tbl;
}
......@@ -119,10 +119,18 @@ static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
int err;
if (voltage == OUTPUT_3V3) {
err = rtsx_pci_write_register(pcr,
SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D);
if (err < 0)
return err;
err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
if (err < 0)
return err;
} else if (voltage == OUTPUT_1V8) {
err = rtsx_pci_write_register(pcr,
SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B);
if (err < 0)
return err;
err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24);
if (err < 0)
return err;
......
......@@ -55,6 +55,7 @@ static DEFINE_PCI_DEVICE_TABLE(rtsx_pci_ids) = {
{ PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ 0, }
};
......@@ -325,7 +326,6 @@ static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr,
val = ((u64)addr << 32) | ((u64)len << 12) | option;
put_unaligned_le64(val, ptr);
ptr++;
pcr->sgi++;
}
......@@ -591,8 +591,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk)
{
int err, clk;
u8 N, min_N, max_N, clk_divider;
u8 mcu_cnt, div, max_div;
u8 n, clk_divider, mcu_cnt, div;
u8 depth[] = {
[RTSX_SSC_DEPTH_4M] = SSC_DEPTH_4M,
[RTSX_SSC_DEPTH_2M] = SSC_DEPTH_2M,
......@@ -616,10 +615,6 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
card_clock /= 1000000;
dev_dbg(&(pcr->pci->dev), "Switch card clock to %dMHz\n", card_clock);
min_N = 80;
max_N = 208;
max_div = CLK_DIV_8;
clk = card_clock;
if (!initial_mode && double_clk)
clk = card_clock * 2;
......@@ -631,30 +626,30 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
return 0;
if (pcr->ops->conv_clk_and_div_n)
N = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N);
n = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N);
else
N = (u8)(clk - 2);
if ((clk <= 2) || (N > max_N))
n = (u8)(clk - 2);
if ((clk <= 2) || (n > MAX_DIV_N_PCR))
return -EINVAL;
mcu_cnt = (u8)(125/clk + 3);
if (mcu_cnt > 15)
mcu_cnt = 15;
/* Make sure that the SSC clock div_n is equal or greater than min_N */
/* Make sure that the SSC clock div_n is not less than MIN_DIV_N_PCR */
div = CLK_DIV_1;
while ((N < min_N) && (div < max_div)) {
while ((n < MIN_DIV_N_PCR) && (div < CLK_DIV_8)) {
if (pcr->ops->conv_clk_and_div_n) {
int dbl_clk = pcr->ops->conv_clk_and_div_n(N,
int dbl_clk = pcr->ops->conv_clk_and_div_n(n,
DIV_N_TO_CLK) * 2;
N = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk,
n = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk,
CLK_TO_DIV_N);
} else {
N = (N + 2) * 2 - 2;
n = (n + 2) * 2 - 2;
}
div++;
}
dev_dbg(&(pcr->pci->dev), "N = %d, div = %d\n", N, div);
dev_dbg(&(pcr->pci->dev), "n = %d, div = %d\n", n, div);
ssc_depth = depth[ssc_depth];
if (double_clk)
......@@ -671,7 +666,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2,
SSC_DEPTH_MASK, ssc_depth);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, N);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB);
if (vpclk) {
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL,
......@@ -713,6 +708,25 @@ int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card)
}
EXPORT_SYMBOL_GPL(rtsx_pci_card_power_off);
int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card)
{
unsigned int cd_mask[] = {
[RTSX_SD_CARD] = SD_EXIST,
[RTSX_MS_CARD] = MS_EXIST
};
if (!pcr->ms_pmos) {
/* When using single PMOS, accessing card is not permitted
* if the existing card is not the designated one.
*/
if (pcr->card_exist & (~cd_mask[card]))
return -EIO;
}
return 0;
}
EXPORT_SYMBOL_GPL(rtsx_pci_card_exclusive_check);
int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
{
if (pcr->ops->switch_output_voltage)
......@@ -758,7 +772,7 @@ static void rtsx_pci_card_detect(struct work_struct *work)
struct delayed_work *dwork;
struct rtsx_pcr *pcr;
unsigned long flags;
unsigned int card_detect = 0;
unsigned int card_detect = 0, card_inserted, card_removed;
u32 irq_status;
dwork = to_delayed_work(work);
......@@ -766,25 +780,35 @@ static void rtsx_pci_card_detect(struct work_struct *work)
dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__);
mutex_lock(&pcr->pcr_mutex);
spin_lock_irqsave(&pcr->lock, flags);
irq_status = rtsx_pci_readl(pcr, RTSX_BIPR);
dev_dbg(&(pcr->pci->dev), "irq_status: 0x%08x\n", irq_status);
if (pcr->card_inserted || pcr->card_removed) {
irq_status &= CARD_EXIST;
card_inserted = pcr->card_inserted & irq_status;
card_removed = pcr->card_removed;
pcr->card_inserted = 0;
pcr->card_removed = 0;
spin_unlock_irqrestore(&pcr->lock, flags);
if (card_inserted || card_removed) {
dev_dbg(&(pcr->pci->dev),
"card_inserted: 0x%x, card_removed: 0x%x\n",
pcr->card_inserted, pcr->card_removed);
card_inserted, card_removed);
if (pcr->ops->cd_deglitch)
pcr->card_inserted = pcr->ops->cd_deglitch(pcr);
card_inserted = pcr->ops->cd_deglitch(pcr);
card_detect = card_inserted | card_removed;
card_detect = pcr->card_inserted | pcr->card_removed;
pcr->card_inserted = 0;
pcr->card_removed = 0;
pcr->card_exist |= card_inserted;
pcr->card_exist &= ~card_removed;
}
spin_unlock_irqrestore(&pcr->lock, flags);
mutex_unlock(&pcr->pcr_mutex);
if ((card_detect & SD_EXIST) && pcr->slots[RTSX_SD_CARD].card_event)
pcr->slots[RTSX_SD_CARD].card_event(
......@@ -836,10 +860,6 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
}
}
if (pcr->card_inserted || pcr->card_removed)
schedule_delayed_work(&pcr->carddet_work,
msecs_to_jiffies(200));
if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) {
if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) {
pcr->trans_result = TRANS_RESULT_FAIL;
......@@ -852,6 +872,10 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
}
}
if (pcr->card_inserted || pcr->card_removed)
schedule_delayed_work(&pcr->carddet_work,
msecs_to_jiffies(200));
spin_unlock(&pcr->lock);
return IRQ_HANDLED;
}
......@@ -974,6 +998,14 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
return err;
}
/* No CD interrupt if probing driver with card inserted.
* So we need to initialize pcr->card_exist here.
*/
if (pcr->ops->cd_deglitch)
pcr->card_exist = pcr->ops->cd_deglitch(pcr);
else
pcr->card_exist = rtsx_pci_readl(pcr, RTSX_BIPR) & CARD_EXIST;
return 0;
}
......@@ -997,6 +1029,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr)
case 0x5289:
rtl8411_init_params(pcr);
break;
case 0x5227:
rts5227_init_params(pcr);
break;
}
dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n",
......@@ -1030,6 +1066,10 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,
pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device,
(int)pcidev->revision);
ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32));
if (ret < 0)
return ret;
ret = pci_enable_device(pcidev);
if (ret)
return ret;
......
......@@ -25,8 +25,12 @@
#include <linux/mfd/rtsx_pci.h>
#define MIN_DIV_N_PCR 80
#define MAX_DIV_N_PCR 208
void rts5209_init_params(struct rtsx_pcr *pcr);
void rts5229_init_params(struct rtsx_pcr *pcr);
void rtl8411_init_params(struct rtsx_pcr *pcr);
void rts5227_init_params(struct rtsx_pcr *pcr);
#endif
......@@ -20,6 +20,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
static struct platform_driver syscon_driver;
......
......@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps6507x.h>
......@@ -116,11 +117,19 @@ static const struct i2c_device_id tps6507x_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);
#ifdef CONFIG_OF
static struct of_device_id tps6507x_of_match[] = {
{.compatible = "ti,tps6507x", },
{},
};
MODULE_DEVICE_TABLE(of, tps6507x_of_match);
#endif
static struct i2c_driver tps6507x_i2c_driver = {
.driver = {
.name = "tps6507x",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(tps6507x_of_match),
},
.probe = tps6507x_i2c_probe,
.remove = tps6507x_i2c_remove,
......
......@@ -25,6 +25,8 @@
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps65090.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/err.h>
#define NUM_INT_REG 2
......@@ -148,18 +150,31 @@ static const struct regmap_config tps65090_regmap_config = {
.volatile_reg = is_volatile_reg,
};
#ifdef CONFIG_OF
static const struct of_device_id tps65090_of_match[] = {
{ .compatible = "ti,tps65090",},
{},
};
MODULE_DEVICE_TABLE(of, tps65090_of_match);
#endif
static int tps65090_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tps65090_platform_data *pdata = client->dev.platform_data;
int irq_base = 0;
struct tps65090 *tps65090;
int ret;
if (!pdata) {
dev_err(&client->dev, "tps65090 requires platform data\n");
if (!pdata && !client->dev.of_node) {
dev_err(&client->dev,
"tps65090 requires platform data or of_node\n");
return -EINVAL;
}
if (pdata)
irq_base = pdata->irq_base;
tps65090 = devm_kzalloc(&client->dev, sizeof(*tps65090), GFP_KERNEL);
if (!tps65090) {
dev_err(&client->dev, "mem alloc for tps65090 failed\n");
......@@ -178,7 +193,7 @@ static int tps65090_i2c_probe(struct i2c_client *client,
if (client->irq) {
ret = regmap_add_irq_chip(tps65090->rmap, client->irq,
IRQF_ONESHOT | IRQF_TRIGGER_LOW, pdata->irq_base,
IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base,
&tps65090_irq_chip, &tps65090->irq_data);
if (ret) {
dev_err(&client->dev,
......@@ -189,7 +204,7 @@ static int tps65090_i2c_probe(struct i2c_client *client,
ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
ARRAY_SIZE(tps65090s), NULL,
regmap_irq_chip_get_base(tps65090->irq_data), NULL);
0, regmap_irq_get_domain(tps65090->irq_data));
if (ret) {
dev_err(&client->dev, "add mfd devices failed with err: %d\n",
ret);
......@@ -215,28 +230,6 @@ static int tps65090_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tps65090_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
if (client->irq)
disable_irq(client->irq);
return 0;
}
static int tps65090_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
if (client->irq)
enable_irq(client->irq);
return 0;
}
#endif
static const struct dev_pm_ops tps65090_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(tps65090_suspend, tps65090_resume)
};
static const struct i2c_device_id tps65090_id_table[] = {
{ "tps65090", 0 },
{ },
......@@ -247,7 +240,7 @@ static struct i2c_driver tps65090_driver = {
.driver = {
.name = "tps65090",
.owner = THIS_MODULE,
.pm = &tps65090_pm_ops,
.of_match_table = of_match_ptr(tps65090_of_match),
},
.probe = tps65090_i2c_probe,
.remove = tps65090_i2c_remove,
......
......@@ -66,16 +66,6 @@
/* Triton Core internal information (BEGIN) */
#define TWL_NUM_SLAVES 4
#define SUB_CHIP_ID0 0
#define SUB_CHIP_ID1 1
#define SUB_CHIP_ID2 2
#define SUB_CHIP_ID3 3
#define SUB_CHIP_ID_INVAL 0xff
#define TWL_MODULE_LAST TWL4030_MODULE_LAST
/* Base Address defns for twl4030_map[] */
/* subchip/slave 0 - USB ID */
......@@ -94,10 +84,7 @@
#define TWL4030_BASEADD_MADC 0x0000
#define TWL4030_BASEADD_MAIN_CHARGE 0x0074
#define TWL4030_BASEADD_PRECHARGE 0x00AA
#define TWL4030_BASEADD_PWM0 0x00F8
#define TWL4030_BASEADD_PWM1 0x00FB
#define TWL4030_BASEADD_PWMA 0x00EF
#define TWL4030_BASEADD_PWMB 0x00F1
#define TWL4030_BASEADD_PWM 0x00F8
#define TWL4030_BASEADD_KEYPAD 0x00D2
#define TWL5031_BASEADD_ACCESSORY 0x0074 /* Replaces Main Charge */
......@@ -117,7 +104,7 @@
/* subchip/slave 0 0x48 - POWER */
#define TWL6030_BASEADD_RTC 0x0000
#define TWL6030_BASEADD_MEM 0x0017
#define TWL6030_BASEADD_SECURED_REG 0x0017
#define TWL6030_BASEADD_PM_MASTER 0x001F
#define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */
#define TWL6030_BASEADD_PM_MISC 0x00E2
......@@ -132,6 +119,7 @@
#define TWL6030_BASEADD_PIH 0x00D0
#define TWL6030_BASEADD_CHARGER 0x00E0
#define TWL6025_BASEADD_CHARGER 0x00DA
#define TWL6030_BASEADD_LED 0x00F4
/* subchip/slave 2 0x4A - DFT */
#define TWL6030_BASEADD_DIEID 0x00C0
......@@ -153,33 +141,28 @@
/*----------------------------------------------------------------------*/
/* is driver active, bound to a chip? */
static bool inuse;
/* TWL IDCODE Register value */
static u32 twl_idcode;
static unsigned int twl_id;
unsigned int twl_rev(void)
{
return twl_id;
}
EXPORT_SYMBOL(twl_rev);
/* Structure for each TWL4030/TWL6030 Slave */
struct twl_client {
struct i2c_client *client;
struct regmap *regmap;
};
static struct twl_client twl_modules[TWL_NUM_SLAVES];
/* mapping the module id to slave id and base address */
struct twl_mapping {
unsigned char sid; /* Slave ID */
unsigned char base; /* base address */
};
static struct twl_mapping *twl_map;
struct twl_private {
bool ready; /* The core driver is ready to be used */
u32 twl_idcode; /* TWL IDCODE Register value */
unsigned int twl_id;
struct twl_mapping *twl_map;
struct twl_client *twl_modules;
};
static struct twl_private *twl_priv;
static struct twl_mapping twl4030_map[] = {
/*
......@@ -188,34 +171,33 @@ static struct twl_mapping twl4030_map[] = {
* so they continue to match the order in this table.
*/
/* Common IPs */
{ 0, TWL4030_BASEADD_USB },
{ 1, TWL4030_BASEADD_PIH },
{ 2, TWL4030_BASEADD_MAIN_CHARGE },
{ 3, TWL4030_BASEADD_PM_MASTER },
{ 3, TWL4030_BASEADD_PM_RECEIVER },
{ 3, TWL4030_BASEADD_RTC },
{ 2, TWL4030_BASEADD_PWM },
{ 2, TWL4030_BASEADD_LED },
{ 3, TWL4030_BASEADD_SECURED_REG },
/* TWL4030 specific IPs */
{ 1, TWL4030_BASEADD_AUDIO_VOICE },
{ 1, TWL4030_BASEADD_GPIO },
{ 1, TWL4030_BASEADD_INTBR },
{ 1, TWL4030_BASEADD_PIH },
{ 1, TWL4030_BASEADD_TEST },
{ 2, TWL4030_BASEADD_KEYPAD },
{ 2, TWL4030_BASEADD_MADC },
{ 2, TWL4030_BASEADD_INTERRUPTS },
{ 2, TWL4030_BASEADD_LED },
{ 2, TWL4030_BASEADD_MAIN_CHARGE },
{ 2, TWL4030_BASEADD_PRECHARGE },
{ 2, TWL4030_BASEADD_PWM0 },
{ 2, TWL4030_BASEADD_PWM1 },
{ 2, TWL4030_BASEADD_PWMA },
{ 2, TWL4030_BASEADD_PWMB },
{ 2, TWL5031_BASEADD_ACCESSORY },
{ 2, TWL5031_BASEADD_INTERRUPTS },
{ 3, TWL4030_BASEADD_BACKUP },
{ 3, TWL4030_BASEADD_INT },
{ 3, TWL4030_BASEADD_PM_MASTER },
{ 3, TWL4030_BASEADD_PM_RECEIVER },
{ 3, TWL4030_BASEADD_RTC },
{ 3, TWL4030_BASEADD_SECURED_REG },
{ 2, TWL5031_BASEADD_ACCESSORY },
{ 2, TWL5031_BASEADD_INTERRUPTS },
};
static struct regmap_config twl4030_regmap_config[4] = {
......@@ -251,35 +233,25 @@ static struct twl_mapping twl6030_map[] = {
* <linux/i2c/twl.h> defines for TWL4030_MODULE_*
* so they continue to match the order in this table.
*/
{ SUB_CHIP_ID1, TWL6030_BASEADD_USB },
{ SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO },
{ SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID1, TWL6030_BASEADD_PIH },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER },
{ SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE },
{ SUB_CHIP_ID1, TWL6030_BASEADD_PWM },
{ SUB_CHIP_ID0, TWL6030_BASEADD_ZERO },
{ SUB_CHIP_ID1, TWL6030_BASEADD_ZERO },
{ SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
{ SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER },
{ SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC },
{ SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
{ SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
{ SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER },
/* Common IPs */
{ 1, TWL6030_BASEADD_USB },
{ 1, TWL6030_BASEADD_PIH },
{ 1, TWL6030_BASEADD_CHARGER },
{ 0, TWL6030_BASEADD_PM_MASTER },
{ 0, TWL6030_BASEADD_PM_SLAVE_MISC },
{ 0, TWL6030_BASEADD_RTC },
{ 1, TWL6030_BASEADD_PWM },
{ 1, TWL6030_BASEADD_LED },
{ 0, TWL6030_BASEADD_SECURED_REG },
/* TWL6030 specific IPs */
{ 0, TWL6030_BASEADD_ZERO },
{ 1, TWL6030_BASEADD_ZERO },
{ 2, TWL6030_BASEADD_ZERO },
{ 1, TWL6030_BASEADD_GPADC_CTRL },
{ 1, TWL6030_BASEADD_GASGAUGE },
};
static struct regmap_config twl6030_regmap_config[3] = {
......@@ -305,8 +277,30 @@ static struct regmap_config twl6030_regmap_config[3] = {
/*----------------------------------------------------------------------*/
static inline int twl_get_num_slaves(void)
{
if (twl_class_is_4030())
return 4; /* TWL4030 class have four slave address */
else
return 3; /* TWL6030 class have three slave address */
}
static inline int twl_get_last_module(void)
{
if (twl_class_is_4030())
return TWL4030_MODULE_LAST;
else
return TWL6030_MODULE_LAST;
}
/* Exported Functions */
unsigned int twl_rev(void)
{
return twl_priv ? twl_priv->twl_id : 0;
}
EXPORT_SYMBOL(twl_rev);
/**
* twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0
* @mod_no: module number
......@@ -314,9 +308,6 @@ static struct regmap_config twl6030_regmap_config[3] = {
* @reg: register address (just offset will do)
* @num_bytes: number of bytes to transfer
*
* IMPORTANT: for 'value' parameter: Allocate value num_bytes+1 and
* valid data starts at Offset 1.
*
* Returns the result of operation - 0 is success
*/
int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
......@@ -325,24 +316,21 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
int sid;
struct twl_client *twl;
if (unlikely(mod_no >= TWL_MODULE_LAST)) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
if (unlikely(!inuse)) {
if (unlikely(!twl_priv || !twl_priv->ready)) {
pr_err("%s: not initialized\n", DRIVER_NAME);
return -EPERM;
}
sid = twl_map[mod_no].sid;
if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
pr_err("%s: module %d is not part of the pmic\n",
DRIVER_NAME, mod_no);
return -EINVAL;
if (unlikely(mod_no >= twl_get_last_module())) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
twl = &twl_modules[sid];
ret = regmap_bulk_write(twl->regmap, twl_map[mod_no].base + reg,
value, num_bytes);
sid = twl_priv->twl_map[mod_no].sid;
twl = &twl_priv->twl_modules[sid];
ret = regmap_bulk_write(twl->regmap,
twl_priv->twl_map[mod_no].base + reg, value,
num_bytes);
if (ret)
pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n",
......@@ -367,24 +355,21 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
int sid;
struct twl_client *twl;
if (unlikely(mod_no >= TWL_MODULE_LAST)) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
if (unlikely(!inuse)) {
if (unlikely(!twl_priv || !twl_priv->ready)) {
pr_err("%s: not initialized\n", DRIVER_NAME);
return -EPERM;
}
sid = twl_map[mod_no].sid;
if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
pr_err("%s: module %d is not part of the pmic\n",
DRIVER_NAME, mod_no);
return -EINVAL;
if (unlikely(mod_no >= twl_get_last_module())) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return -EPERM;
}
twl = &twl_modules[sid];
ret = regmap_bulk_read(twl->regmap, twl_map[mod_no].base + reg,
value, num_bytes);
sid = twl_priv->twl_map[mod_no].sid;
twl = &twl_priv->twl_modules[sid];
ret = regmap_bulk_read(twl->regmap,
twl_priv->twl_map[mod_no].base + reg, value,
num_bytes);
if (ret)
pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n",
......@@ -394,34 +379,6 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
}
EXPORT_SYMBOL(twl_i2c_read);
/**
* twl_i2c_write_u8 - Writes a 8 bit register in TWL4030/TWL5030/TWL60X0
* @mod_no: module number
* @value: the value to be written 8 bit
* @reg: register address (just offset will do)
*
* Returns result of operation - 0 is success
*/
int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
{
return twl_i2c_write(mod_no, &value, reg, 1);
}
EXPORT_SYMBOL(twl_i2c_write_u8);
/**
* twl_i2c_read_u8 - Reads a 8 bit register from TWL4030/TWL5030/TWL60X0
* @mod_no: module number
* @value: the value read 8 bit
* @reg: register address (just offset will do)
*
* Returns result of operation - 0 is success
*/
int twl_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
{
return twl_i2c_read(mod_no, value, reg, 1);
}
EXPORT_SYMBOL(twl_i2c_read_u8);
/*----------------------------------------------------------------------*/
/**
......@@ -440,7 +397,7 @@ static int twl_read_idcode_register(void)
goto fail;
}
err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_idcode),
err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_priv->twl_idcode),
REG_IDCODE_7_0, 4);
if (err) {
pr_err("TWL4030: unable to read IDCODE -%d\n", err);
......@@ -461,7 +418,7 @@ static int twl_read_idcode_register(void)
*/
int twl_get_type(void)
{
return TWL_SIL_TYPE(twl_idcode);
return TWL_SIL_TYPE(twl_priv->twl_idcode);
}
EXPORT_SYMBOL_GPL(twl_get_type);
......@@ -472,7 +429,7 @@ EXPORT_SYMBOL_GPL(twl_get_type);
*/
int twl_get_version(void)
{
return TWL_SIL_REV(twl_idcode);
return TWL_SIL_REV(twl_priv->twl_idcode);
}
EXPORT_SYMBOL_GPL(twl_get_version);
......@@ -509,13 +466,20 @@ int twl_get_hfclk_rate(void)
EXPORT_SYMBOL_GPL(twl_get_hfclk_rate);
static struct device *
add_numbered_child(unsigned chip, const char *name, int num,
add_numbered_child(unsigned mod_no, const char *name, int num,
void *pdata, unsigned pdata_len,
bool can_wakeup, int irq0, int irq1)
{
struct platform_device *pdev;
struct twl_client *twl = &twl_modules[chip];
int status;
struct twl_client *twl;
int status, sid;
if (unlikely(mod_no >= twl_get_last_module())) {
pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
return ERR_PTR(-EPERM);
}
sid = twl_priv->twl_map[mod_no].sid;
twl = &twl_priv->twl_modules[sid];
pdev = platform_device_alloc(name, num);
if (!pdev) {
......@@ -560,11 +524,11 @@ add_numbered_child(unsigned chip, const char *name, int num,
return &pdev->dev;
}
static inline struct device *add_child(unsigned chip, const char *name,
static inline struct device *add_child(unsigned mod_no, const char *name,
void *pdata, unsigned pdata_len,
bool can_wakeup, int irq0, int irq1)
{
return add_numbered_child(chip, name, -1, pdata, pdata_len,
return add_numbered_child(mod_no, name, -1, pdata, pdata_len,
can_wakeup, irq0, irq1);
}
......@@ -573,7 +537,6 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
struct regulator_consumer_supply *consumers,
unsigned num_consumers, unsigned long features)
{
unsigned sub_chip_id;
struct twl_regulator_driver_data drv_data;
/* regulator framework demands init_data ... */
......@@ -600,8 +563,7 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
}
/* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */
sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid;
return add_numbered_child(sub_chip_id, "twl_reg", num,
return add_numbered_child(TWL_MODULE_PM_MASTER, "twl_reg", num,
pdata, sizeof(*pdata), false, 0, 0);
}
......@@ -623,10 +585,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
unsigned long features)
{
struct device *child;
unsigned sub_chip_id;
if (IS_ENABLED(CONFIG_GPIO_TWL4030) && pdata->gpio) {
child = add_child(SUB_CHIP_ID1, "twl4030_gpio",
child = add_child(TWL4030_MODULE_GPIO, "twl4030_gpio",
pdata->gpio, sizeof(*pdata->gpio),
false, irq_base + GPIO_INTR_OFFSET, 0);
if (IS_ERR(child))
......@@ -634,7 +595,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
}
if (IS_ENABLED(CONFIG_KEYBOARD_TWL4030) && pdata->keypad) {
child = add_child(SUB_CHIP_ID2, "twl4030_keypad",
child = add_child(TWL4030_MODULE_KEYPAD, "twl4030_keypad",
pdata->keypad, sizeof(*pdata->keypad),
true, irq_base + KEYPAD_INTR_OFFSET, 0);
if (IS_ERR(child))
......@@ -643,7 +604,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc &&
twl_class_is_4030()) {
child = add_child(SUB_CHIP_ID2, "twl4030_madc",
child = add_child(TWL4030_MODULE_MADC, "twl4030_madc",
pdata->madc, sizeof(*pdata->madc),
true, irq_base + MADC_INTR_OFFSET, 0);
if (IS_ERR(child))
......@@ -658,22 +619,21 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
* Eventually, Linux might become more aware of such
* HW security concerns, and "least privilege".
*/
sub_chip_id = twl_map[TWL_MODULE_RTC].sid;
child = add_child(sub_chip_id, "twl_rtc", NULL, 0,
child = add_child(TWL_MODULE_RTC, "twl_rtc", NULL, 0,
true, irq_base + RTC_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (IS_ENABLED(CONFIG_PWM_TWL)) {
child = add_child(SUB_CHIP_ID1, "twl-pwm", NULL, 0,
child = add_child(TWL_MODULE_PWM, "twl-pwm", NULL, 0,
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (IS_ENABLED(CONFIG_PWM_TWL_LED)) {
child = add_child(SUB_CHIP_ID1, "twl-pwmled", NULL, 0,
child = add_child(TWL_MODULE_LED, "twl-pwmled", NULL, 0,
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
......@@ -725,7 +685,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
}
child = add_child(SUB_CHIP_ID0, "twl4030_usb",
child = add_child(TWL_MODULE_USB, "twl4030_usb",
pdata->usb, sizeof(*pdata->usb), true,
/* irq0 = USB_PRES, irq1 = USB */
irq_base + USB_PRES_INTR_OFFSET,
......@@ -774,7 +734,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
pdata->usb->features = features;
child = add_child(SUB_CHIP_ID0, "twl6030_usb",
child = add_child(TWL_MODULE_USB, "twl6030_usb",
pdata->usb, sizeof(*pdata->usb), true,
/* irq1 = VBUS_PRES, irq0 = USB ID */
irq_base + USBOTG_INTR_OFFSET,
......@@ -799,22 +759,22 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
}
if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) {
child = add_child(SUB_CHIP_ID3, "twl4030_wdt", NULL, 0,
false, 0, 0);
child = add_child(TWL_MODULE_PM_RECEIVER, "twl4030_wdt", NULL,
0, false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (IS_ENABLED(CONFIG_INPUT_TWL4030_PWRBUTTON) && twl_class_is_4030()) {
child = add_child(SUB_CHIP_ID3, "twl4030_pwrbutton", NULL, 0,
true, irq_base + 8 + 0, 0);
child = add_child(TWL_MODULE_PM_MASTER, "twl4030_pwrbutton",
NULL, 0, true, irq_base + 8 + 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (IS_ENABLED(CONFIG_MFD_TWL4030_AUDIO) && pdata->audio &&
twl_class_is_4030()) {
child = add_child(SUB_CHIP_ID1, "twl4030-audio",
child = add_child(TWL4030_MODULE_AUDIO_VOICE, "twl4030-audio",
pdata->audio, sizeof(*pdata->audio),
false, 0, 0);
if (IS_ERR(child))
......@@ -1054,7 +1014,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci &&
!(features & (TPS_SUBSET | TWL5031))) {
child = add_child(SUB_CHIP_ID3, "twl4030_bci",
child = add_child(TWL_MODULE_MAIN_CHARGE, "twl4030_bci",
pdata->bci, sizeof(*pdata->bci), false,
/* irq0 = CHG_PRES, irq1 = BCI */
irq_base + BCI_PRES_INTR_OFFSET,
......@@ -1145,25 +1105,23 @@ static int twl_remove(struct i2c_client *client)
unsigned i, num_slaves;
int status;
if (twl_class_is_4030()) {
if (twl_class_is_4030())
status = twl4030_exit_irq();
num_slaves = TWL_NUM_SLAVES;
} else {
else
status = twl6030_exit_irq();
num_slaves = TWL_NUM_SLAVES - 1;
}
if (status < 0)
return status;
num_slaves = twl_get_num_slaves();
for (i = 0; i < num_slaves; i++) {
struct twl_client *twl = &twl_modules[i];
struct twl_client *twl = &twl_priv->twl_modules[i];
if (twl->client && twl->client != client)
i2c_unregister_device(twl->client);
twl_modules[i].client = NULL;
twl->client = NULL;
}
inuse = false;
twl_priv->ready = false;
return 0;
}
......@@ -1179,6 +1137,17 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
int status;
unsigned i, num_slaves;
if (!node && !pdata) {
dev_err(&client->dev, "no platform data\n");
return -EINVAL;
}
if (twl_priv) {
dev_dbg(&client->dev, "only one instance of %s allowed\n",
DRIVER_NAME);
return -EBUSY;
}
pdev = platform_device_alloc(DRIVER_NAME, -1);
if (!pdev) {
dev_err(&client->dev, "can't alloc pdev\n");
......@@ -1191,54 +1160,44 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
return status;
}
if (node && !pdata) {
/*
* XXX: Temporary pdata until the information is correctly
* retrieved by every TWL modules from DT.
*/
pdata = devm_kzalloc(&client->dev,
sizeof(struct twl4030_platform_data),
GFP_KERNEL);
if (!pdata) {
status = -ENOMEM;
goto free;
}
}
if (!pdata) {
dev_dbg(&client->dev, "no platform data?\n");
status = -EINVAL;
goto free;
}
platform_set_drvdata(pdev, pdata);
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
dev_dbg(&client->dev, "can't talk I2C?\n");
status = -EIO;
goto free;
}
if (inuse) {
dev_dbg(&client->dev, "driver is already in use\n");
status = -EBUSY;
twl_priv = devm_kzalloc(&client->dev, sizeof(struct twl_private),
GFP_KERNEL);
if (!twl_priv) {
status = -ENOMEM;
goto free;
}
if ((id->driver_data) & TWL6030_CLASS) {
twl_id = TWL6030_CLASS_ID;
twl_map = &twl6030_map[0];
twl_priv->twl_id = TWL6030_CLASS_ID;
twl_priv->twl_map = &twl6030_map[0];
/* The charger base address is different in twl6025 */
if ((id->driver_data) & TWL6025_SUBCLASS)
twl_priv->twl_map[TWL_MODULE_MAIN_CHARGE].base =
TWL6025_BASEADD_CHARGER;
twl_regmap_config = twl6030_regmap_config;
num_slaves = TWL_NUM_SLAVES - 1;
} else {
twl_id = TWL4030_CLASS_ID;
twl_map = &twl4030_map[0];
twl_priv->twl_id = TWL4030_CLASS_ID;
twl_priv->twl_map = &twl4030_map[0];
twl_regmap_config = twl4030_regmap_config;
num_slaves = TWL_NUM_SLAVES;
}
num_slaves = twl_get_num_slaves();
twl_priv->twl_modules = devm_kzalloc(&client->dev,
sizeof(struct twl_client) * num_slaves,
GFP_KERNEL);
if (!twl_priv->twl_modules) {
status = -ENOMEM;
goto free;
}
for (i = 0; i < num_slaves; i++) {
struct twl_client *twl = &twl_modules[i];
struct twl_client *twl = &twl_priv->twl_modules[i];
if (i == 0) {
twl->client = client;
......@@ -1264,19 +1223,19 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
}
inuse = true;
twl_priv->ready = true;
/* setup clock framework */
clocks_init(&pdev->dev, pdata->clock);
clocks_init(&pdev->dev, pdata ? pdata->clock : NULL);
/* read TWL IDCODE Register */
if (twl_id == TWL4030_CLASS_ID) {
if (twl_class_is_4030()) {
status = twl_read_idcode_register();
WARN(status < 0, "Error: reading twl_idcode register value\n");
}
/* load power event scripts */
if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata->power)
if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata && pdata->power)
twl4030_power_init(pdata->power);
/* Maybe init the T2 Interrupt subsystem */
......@@ -1308,10 +1267,9 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
}
status = -ENODEV;
if (node)
status = of_platform_populate(node, NULL, NULL, &client->dev);
if (status)
else
status = add_children(pdata, irq_base, id->driver_data);
fail:
......
......@@ -49,6 +49,8 @@
#define SYS_ID_HBI_SHIFT 16
#define SYS_PROCIDx_HBI_SHIFT 0
#define SYS_LED_LED(n) (1 << (n))
#define SYS_MCI_CARDIN (1 << 0)
#define SYS_MCI_WPROT (1 << 1)
......@@ -336,34 +338,40 @@ void __init vexpress_sysreg_early_init(void __iomem *base)
void __init vexpress_sysreg_of_early_init(void)
{
struct device_node *node = of_find_compatible_node(NULL, NULL,
"arm,vexpress-sysreg");
struct device_node *node;
if (vexpress_sysreg_base)
return;
node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg");
if (node) {
vexpress_sysreg_base = of_iomap(node, 0);
vexpress_sysreg_setup(node);
} else {
pr_info("vexpress-sysreg: No Device Tree node found.");
}
}
#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \
[VEXPRESS_GPIO_##_name] = { \
.reg = _reg, \
.value = _reg##_##_value, \
}
static struct vexpress_sysreg_gpio {
unsigned long reg;
u32 value;
} vexpress_sysreg_gpios[] = {
[VEXPRESS_GPIO_MMC_CARDIN] = {
.reg = SYS_MCI,
.value = SYS_MCI_CARDIN,
},
[VEXPRESS_GPIO_MMC_WPROT] = {
.reg = SYS_MCI,
.value = SYS_MCI_WPROT,
},
[VEXPRESS_GPIO_FLASH_WPn] = {
.reg = SYS_FLASH,
.value = SYS_FLASH_WPn,
},
VEXPRESS_SYSREG_GPIO(MMC_CARDIN, SYS_MCI, CARDIN),
VEXPRESS_SYSREG_GPIO(MMC_WPROT, SYS_MCI, WPROT),
VEXPRESS_SYSREG_GPIO(FLASH_WPn, SYS_FLASH, WPn),
VEXPRESS_SYSREG_GPIO(LED0, SYS_LED, LED(0)),
VEXPRESS_SYSREG_GPIO(LED1, SYS_LED, LED(1)),
VEXPRESS_SYSREG_GPIO(LED2, SYS_LED, LED(2)),
VEXPRESS_SYSREG_GPIO(LED3, SYS_LED, LED(3)),
VEXPRESS_SYSREG_GPIO(LED4, SYS_LED, LED(4)),
VEXPRESS_SYSREG_GPIO(LED5, SYS_LED, LED(5)),
VEXPRESS_SYSREG_GPIO(LED6, SYS_LED, LED(6)),
VEXPRESS_SYSREG_GPIO(LED7, SYS_LED, LED(7)),
};
static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip,
......@@ -372,12 +380,6 @@ static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip,
return 0;
}
static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
return 0;
}
static int vexpress_sysreg_gpio_get(struct gpio_chip *chip,
unsigned offset)
{
......@@ -401,6 +403,14 @@ static void vexpress_sysreg_gpio_set(struct gpio_chip *chip,
writel(reg_value, vexpress_sysreg_base + gpio->reg);
}
static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
vexpress_sysreg_gpio_set(chip, offset, value);
return 0;
}
static struct gpio_chip vexpress_sysreg_gpio_chip = {
.label = "vexpress-sysreg",
.direction_input = vexpress_sysreg_gpio_direction_input,
......@@ -412,6 +422,30 @@ static struct gpio_chip vexpress_sysreg_gpio_chip = {
};
#define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \
{ \
.name = "v2m:green:"_name, \
.default_trigger = _default_trigger, \
.gpio = VEXPRESS_GPIO_##_gpio, \
}
struct gpio_led vexpress_sysreg_leds[] = {
VEXPRESS_SYSREG_GREEN_LED("user1", "heartbeat", LED0),
VEXPRESS_SYSREG_GREEN_LED("user2", "mmc0", LED1),
VEXPRESS_SYSREG_GREEN_LED("user3", "cpu0", LED2),
VEXPRESS_SYSREG_GREEN_LED("user4", "cpu1", LED3),
VEXPRESS_SYSREG_GREEN_LED("user5", "cpu2", LED4),
VEXPRESS_SYSREG_GREEN_LED("user6", "cpu3", LED5),
VEXPRESS_SYSREG_GREEN_LED("user7", "cpu4", LED6),
VEXPRESS_SYSREG_GREEN_LED("user8", "cpu5", LED7),
};
struct gpio_led_platform_data vexpress_sysreg_leds_pdata = {
.num_leds = ARRAY_SIZE(vexpress_sysreg_leds),
.leds = vexpress_sysreg_leds,
};
static ssize_t vexpress_sysreg_sys_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
......@@ -456,6 +490,10 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
return err;
}
platform_device_register_data(vexpress_sysreg_dev, "leds-gpio",
PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata,
sizeof(vexpress_sysreg_leds_pdata));
vexpress_sysreg_dev = &pdev->dev;
device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id);
......@@ -478,6 +516,7 @@ static struct platform_driver vexpress_sysreg_driver = {
static int __init vexpress_sysreg_init(void)
{
vexpress_sysreg_of_early_init();
return platform_driver_register(&vexpress_sysreg_driver);
}
core_initcall(vexpress_sysreg_init);
......@@ -59,12 +59,13 @@ static const struct reg_default wm5102_reva_patch[] = {
static const struct reg_default wm5102_revb_patch[] = {
{ 0x80, 0x0003 },
{ 0x081, 0xE022 },
{ 0x410, 0x6080 },
{ 0x418, 0x6080 },
{ 0x420, 0x6080 },
{ 0x410, 0x4080 },
{ 0x418, 0x4080 },
{ 0x420, 0x4080 },
{ 0x428, 0xC000 },
{ 0x441, 0x8014 },
{ 0x4B0, 0x0066 },
{ 0x458, 0x000b },
{ 0x212, 0x0000 },
{ 0x80, 0x0000 },
};
......@@ -231,11 +232,9 @@ const struct regmap_irq_chip wm5102_irq = {
static const struct reg_default wm5102_reg_default[] = {
{ 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */
{ 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */
{ 0x0000000D, 0x0000 }, /* R13 - Ctrl IF Status 1 */
{ 0x00000016, 0x0000 }, /* R22 - Write Sequencer Ctrl 0 */
{ 0x00000017, 0x0000 }, /* R23 - Write Sequencer Ctrl 1 */
{ 0x00000018, 0x0000 }, /* R24 - Write Sequencer Ctrl 2 */
{ 0x0000001A, 0x0000 }, /* R26 - Write Sequencer PROM */
{ 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */
{ 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */
{ 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */
......@@ -250,12 +249,14 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
{ 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
{ 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
{ 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */
{ 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */
{ 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */
{ 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */
{ 0x0000006C, 0x01FF }, /* R108 - Always On Triggers Sequence Select 5 */
{ 0x0000006D, 0x01FF }, /* R109 - Always On Triggers Sequence Select 6 */
{ 0x00000066, 0x01FF }, /* R102 - Always On Triggers Sequence Select 1 */
{ 0x00000067, 0x01FF }, /* R103 - Always On Triggers Sequence Select 2 */
{ 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 3 */
{ 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 4 */
{ 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 5 */
{ 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 6 */
{ 0x0000006E, 0x01FF }, /* R110 - Trigger Sequence Select 32 */
{ 0x0000006F, 0x01FF }, /* R111 - Trigger Sequence Select 33 */
{ 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */
{ 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
{ 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */
......@@ -265,13 +266,14 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000095, 0x0000 }, /* R149 - Haptics phase 2 duration */
{ 0x00000096, 0x0000 }, /* R150 - Haptics phase 3 intensity */
{ 0x00000097, 0x0000 }, /* R151 - Haptics phase 3 duration */
{ 0x00000100, 0x0001 }, /* R256 - Clock 32k 1 */
{ 0x00000100, 0x0002 }, /* R256 - Clock 32k 1 */
{ 0x00000101, 0x0304 }, /* R257 - System Clock 1 */
{ 0x00000102, 0x0011 }, /* R258 - Sample rate 1 */
{ 0x00000103, 0x0011 }, /* R259 - Sample rate 2 */
{ 0x00000104, 0x0011 }, /* R260 - Sample rate 3 */
{ 0x00000112, 0x0305 }, /* R274 - Async clock 1 */
{ 0x00000113, 0x0011 }, /* R275 - Async sample rate 1 */
{ 0x00000114, 0x0011 }, /* R276 - Async sample rate 2 */
{ 0x00000149, 0x0000 }, /* R329 - Output system clock */
{ 0x0000014A, 0x0000 }, /* R330 - Output async clock */
{ 0x00000152, 0x0000 }, /* R338 - Rate Estimator 1 */
......@@ -280,13 +282,14 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */
{ 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */
{ 0x00000161, 0x0000 }, /* R353 - Dynamic Frequency Scaling 1 */
{ 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */
{ 0x00000171, 0x0002 }, /* R369 - FLL1 Control 1 */
{ 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */
{ 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */
{ 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */
{ 0x00000175, 0x0004 }, /* R373 - FLL1 Control 5 */
{ 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */
{ 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */
{ 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */
{ 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */
{ 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */
{ 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */
......@@ -302,6 +305,7 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000195, 0x0004 }, /* R405 - FLL2 Control 5 */
{ 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */
{ 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */
{ 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */
{ 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */
{ 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */
{ 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */
......@@ -317,8 +321,12 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */
{ 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
{ 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
{ 0x00000225, 0x0400 }, /* R549 - HP Ctrl 1L */
{ 0x00000226, 0x0400 }, /* R550 - HP Ctrl 1R */
{ 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */
{ 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */
{ 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */
{ 0x0000029F, 0x0000 }, /* R671 - Headphone Detect Test */
{ 0x000002A2, 0x0000 }, /* R674 - Micd clamp control */
{ 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
{ 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */
......@@ -350,53 +358,44 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */
{ 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */
{ 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */
{ 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */
{ 0x00000410, 0x4080 }, /* R1040 - Output Path Config 1L */
{ 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */
{ 0x00000412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */
{ 0x00000412, 0x0081 }, /* R1042 - DAC Volume Limit 1L */
{ 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */
{ 0x00000414, 0x0080 }, /* R1044 - Output Path Config 1R */
{ 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */
{ 0x00000416, 0x0080 }, /* R1046 - DAC Volume Limit 1R */
{ 0x00000416, 0x0081 }, /* R1046 - DAC Volume Limit 1R */
{ 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */
{ 0x00000418, 0x0080 }, /* R1048 - Output Path Config 2L */
{ 0x00000418, 0x4080 }, /* R1048 - Output Path Config 2L */
{ 0x00000419, 0x0180 }, /* R1049 - DAC Digital Volume 2L */
{ 0x0000041A, 0x0080 }, /* R1050 - DAC Volume Limit 2L */
{ 0x0000041A, 0x0081 }, /* R1050 - DAC Volume Limit 2L */
{ 0x0000041B, 0x0004 }, /* R1051 - Noise Gate Select 2L */
{ 0x0000041C, 0x0080 }, /* R1052 - Output Path Config 2R */
{ 0x0000041D, 0x0180 }, /* R1053 - DAC Digital Volume 2R */
{ 0x0000041E, 0x0080 }, /* R1054 - DAC Volume Limit 2R */
{ 0x0000041E, 0x0081 }, /* R1054 - DAC Volume Limit 2R */
{ 0x0000041F, 0x0008 }, /* R1055 - Noise Gate Select 2R */
{ 0x00000420, 0x0080 }, /* R1056 - Output Path Config 3L */
{ 0x00000420, 0x4080 }, /* R1056 - Output Path Config 3L */
{ 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */
{ 0x00000422, 0x0080 }, /* R1058 - DAC Volume Limit 3L */
{ 0x00000422, 0x0081 }, /* R1058 - DAC Volume Limit 3L */
{ 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */
{ 0x00000424, 0x0080 }, /* R1060 - Output Path Config 3R */
{ 0x00000425, 0x0180 }, /* R1061 - DAC Digital Volume 3R */
{ 0x00000426, 0x0080 }, /* R1062 - DAC Volume Limit 3R */
{ 0x00000428, 0x0000 }, /* R1064 - Output Path Config 4L */
{ 0x00000428, 0xC000 }, /* R1064 - Output Path Config 4L */
{ 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */
{ 0x0000042A, 0x0080 }, /* R1066 - Out Volume 4L */
{ 0x0000042A, 0x0081 }, /* R1066 - Out Volume 4L */
{ 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */
{ 0x0000042C, 0x0000 }, /* R1068 - Output Path Config 4R */
{ 0x0000042D, 0x0180 }, /* R1069 - DAC Digital Volume 4R */
{ 0x0000042E, 0x0080 }, /* R1070 - Out Volume 4R */
{ 0x0000042E, 0x0081 }, /* R1070 - Out Volume 4R */
{ 0x0000042F, 0x0080 }, /* R1071 - Noise Gate Select 4R */
{ 0x00000430, 0x0000 }, /* R1072 - Output Path Config 5L */
{ 0x00000431, 0x0180 }, /* R1073 - DAC Digital Volume 5L */
{ 0x00000432, 0x0080 }, /* R1074 - DAC Volume Limit 5L */
{ 0x00000432, 0x0081 }, /* R1074 - DAC Volume Limit 5L */
{ 0x00000433, 0x0100 }, /* R1075 - Noise Gate Select 5L */
{ 0x00000434, 0x0000 }, /* R1076 - Output Path Config 5R */
{ 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */
{ 0x00000436, 0x0080 }, /* R1078 - DAC Volume Limit 5R */
{ 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */
{ 0x00000436, 0x0081 }, /* R1078 - DAC Volume Limit 5R */
{ 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */
{ 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */
{ 0x00000458, 0x0001 }, /* R1112 - Noise Gate Control */
{ 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */
{ 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */
{ 0x000004DC, 0x0000 }, /* R1244 - DAC comp 1 */
{ 0x000004DD, 0x0000 }, /* R1245 - DAC comp 2 */
{ 0x000004DE, 0x0000 }, /* R1246 - DAC comp 3 */
{ 0x000004DF, 0x0000 }, /* R1247 - DAC comp 4 */
{ 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */
{ 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */
{ 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */
......@@ -424,7 +423,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000518, 0x0007 }, /* R1304 - AIF1 Frame Ctrl 18 */
{ 0x00000519, 0x0000 }, /* R1305 - AIF1 Tx Enables */
{ 0x0000051A, 0x0000 }, /* R1306 - AIF1 Rx Enables */
{ 0x0000051B, 0x0000 }, /* R1307 - AIF1 Force Write */
{ 0x00000540, 0x000C }, /* R1344 - AIF2 BCLK Ctrl */
{ 0x00000541, 0x0008 }, /* R1345 - AIF2 Tx Pin Ctrl */
{ 0x00000542, 0x0000 }, /* R1346 - AIF2 Rx Pin Ctrl */
......@@ -440,7 +438,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */
{ 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */
{ 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */
{ 0x0000055B, 0x0000 }, /* R1371 - AIF2 Force Write */
{ 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */
{ 0x00000581, 0x0008 }, /* R1409 - AIF3 Tx Pin Ctrl */
{ 0x00000582, 0x0000 }, /* R1410 - AIF3 Rx Pin Ctrl */
......@@ -456,7 +453,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000592, 0x0001 }, /* R1426 - AIF3 Frame Ctrl 12 */
{ 0x00000599, 0x0000 }, /* R1433 - AIF3 Tx Enables */
{ 0x0000059A, 0x0000 }, /* R1434 - AIF3 Rx Enables */
{ 0x0000059B, 0x0000 }, /* R1435 - AIF3 Force Write */
{ 0x000005E3, 0x0004 }, /* R1507 - SLIMbus Framer Ref Gear */
{ 0x000005E5, 0x0000 }, /* R1509 - SLIMbus Rates 1 */
{ 0x000005E6, 0x0000 }, /* R1510 - SLIMbus Rates 2 */
......@@ -780,22 +776,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x000008CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */
{ 0x000008CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */
{ 0x000008CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */
{ 0x000008D0, 0x0000 }, /* R2256 - DRC2LMIX Input 1 Source */
{ 0x000008D1, 0x0080 }, /* R2257 - DRC2LMIX Input 1 Volume */
{ 0x000008D2, 0x0000 }, /* R2258 - DRC2LMIX Input 2 Source */
{ 0x000008D3, 0x0080 }, /* R2259 - DRC2LMIX Input 2 Volume */
{ 0x000008D4, 0x0000 }, /* R2260 - DRC2LMIX Input 3 Source */
{ 0x000008D5, 0x0080 }, /* R2261 - DRC2LMIX Input 3 Volume */
{ 0x000008D6, 0x0000 }, /* R2262 - DRC2LMIX Input 4 Source */
{ 0x000008D7, 0x0080 }, /* R2263 - DRC2LMIX Input 4 Volume */
{ 0x000008D8, 0x0000 }, /* R2264 - DRC2RMIX Input 1 Source */
{ 0x000008D9, 0x0080 }, /* R2265 - DRC2RMIX Input 1 Volume */
{ 0x000008DA, 0x0000 }, /* R2266 - DRC2RMIX Input 2 Source */
{ 0x000008DB, 0x0080 }, /* R2267 - DRC2RMIX Input 2 Volume */
{ 0x000008DC, 0x0000 }, /* R2268 - DRC2RMIX Input 3 Source */
{ 0x000008DD, 0x0080 }, /* R2269 - DRC2RMIX Input 3 Volume */
{ 0x000008DE, 0x0000 }, /* R2270 - DRC2RMIX Input 4 Source */
{ 0x000008DF, 0x0080 }, /* R2271 - DRC2RMIX Input 4 Volume */
{ 0x00000900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */
{ 0x00000901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */
{ 0x00000902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */
......@@ -887,7 +867,7 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */
{ 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */
{ 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */
{ 0x00000D41, 0x0000 }, /* R3393 - ADSP2 IRQ0 */
{ 0x00000D50, 0x0000 }, /* R3408 - AOD wkup and trig */
{ 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */
{ 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */
{ 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */
......@@ -982,11 +962,6 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */
{ 0x00000E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */
{ 0x00000E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */
{ 0x00000E89, 0x0018 }, /* R3721 - DRC2 ctrl1 */
{ 0x00000E8A, 0x0933 }, /* R3722 - DRC2 ctrl2 */
{ 0x00000E8B, 0x0018 }, /* R3723 - DRC2 ctrl3 */
{ 0x00000E8C, 0x0000 }, /* R3724 - DRC2 ctrl4 */
{ 0x00000E8D, 0x0000 }, /* R3725 - DRC2 ctrl5 */
{ 0x00000EC0, 0x0000 }, /* R3776 - HPLPF1_1 */
{ 0x00000EC1, 0x0000 }, /* R3777 - HPLPF1_2 */
{ 0x00000EC4, 0x0000 }, /* R3780 - HPLPF2_1 */
......@@ -997,16 +972,12 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */
{ 0x00000EE0, 0x0000 }, /* R3808 - ASRC_ENABLE */
{ 0x00000EE2, 0x0000 }, /* R3810 - ASRC_RATE1 */
{ 0x00000EE3, 0x4000 }, /* R3811 - ASRC_RATE2 */
{ 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */
{ 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */
{ 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */
{ 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */
{ 0x00000EF4, 0x0000 }, /* R3828 - ISRC 2 CTRL 2 */
{ 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */
{ 0x00000EF6, 0x0000 }, /* R3830 - ISRC 3 CTRL 1 */
{ 0x00000EF7, 0x0000 }, /* R3831 - ISRC 3 CTRL 2 */
{ 0x00000EF8, 0x0000 }, /* R3832 - ISRC 3 CTRL 3 */
{ 0x00001100, 0x0010 }, /* R4352 - DSP1 Control 1 */
{ 0x00001101, 0x0000 }, /* R4353 - DSP1 Clocking 1 */
};
......@@ -1833,17 +1804,24 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
case ARIZONA_DSP1_SCRATCH_0:
case ARIZONA_DSP1_SCRATCH_1:
case ARIZONA_DSP1_SCRATCH_2:
case ARIZONA_DSP1_SCRATCH_3:
return true;
default:
return false;
if ((reg >= 0x100000 && reg < 0x106000) ||
(reg >= 0x180000 && reg < 0x180800) ||
(reg >= 0x190000 && reg < 0x194800) ||
(reg >= 0x1a8000 && reg < 0x1a9800))
return true;
else
return false;
}
}
static bool wm5102_volatile_register(struct device *dev, unsigned int reg)
{
if (reg > 0xffff)
return true;
switch (reg) {
case ARIZONA_SOFTWARE_RESET:
case ARIZONA_DEVICE_REVISION:
......@@ -1884,12 +1862,22 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg)
case ARIZONA_DSP1_STATUS_1:
case ARIZONA_DSP1_STATUS_2:
case ARIZONA_DSP1_STATUS_3:
case ARIZONA_DSP1_SCRATCH_0:
case ARIZONA_DSP1_SCRATCH_1:
case ARIZONA_DSP1_SCRATCH_2:
case ARIZONA_DSP1_SCRATCH_3:
case ARIZONA_HEADPHONE_DETECT_2:
case ARIZONA_HP_DACVAL:
case ARIZONA_MIC_DETECT_3:
return true;
default:
return false;
if ((reg >= 0x100000 && reg < 0x106000) ||
(reg >= 0x180000 && reg < 0x180800) ||
(reg >= 0x190000 && reg < 0x194800) ||
(reg >= 0x1a8000 && reg < 0x1a9800))
return true;
else
return false;
}
}
......
......@@ -467,7 +467,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
goto err;
}
ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
ret = devm_regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
wm8994->supplies);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret);
......@@ -478,7 +478,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
wm8994->supplies);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret);
goto err_get;
goto err;
}
ret = wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET);
......@@ -658,8 +658,6 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
err_enable:
regulator_bulk_disable(wm8994->num_supplies,
wm8994->supplies);
err_get:
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
err:
mfd_remove_devices(wm8994->dev);
return ret;
......@@ -672,7 +670,6 @@ static void wm8994_device_exit(struct wm8994 *wm8994)
wm8994_irq_exit(wm8994);
regulator_bulk_disable(wm8994->num_supplies,
wm8994->supplies);
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
}
static const struct of_device_id wm8994_of_match[] = {
......
......@@ -678,12 +678,19 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct mmc_command *cmd = mrq->cmd;
struct mmc_data *data = mrq->data;
unsigned int data_size = 0;
int err;
if (host->eject) {
cmd->error = -ENOMEDIUM;
goto finish;
}
err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD);
if (err) {
cmd->error = err;
goto finish;
}
mutex_lock(&pcr->pcr_mutex);
rtsx_pci_start_run(pcr);
......@@ -901,6 +908,9 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->eject)
return;
if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD))
return;
mutex_lock(&pcr->pcr_mutex);
rtsx_pci_start_run(pcr);
......@@ -1073,6 +1083,10 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->eject)
return -ENOMEDIUM;
err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD);
if (err)
return err;
mutex_lock(&pcr->pcr_mutex);
rtsx_pci_start_run(pcr);
......@@ -1122,6 +1136,10 @@ static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (host->eject)
return -ENOMEDIUM;
err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD);
if (err)
return err;
mutex_lock(&pcr->pcr_mutex);
rtsx_pci_start_run(pcr);
......
......@@ -305,6 +305,16 @@ config RTC_DRV_X1205
This driver can also be built as a module. If so, the module
will be called rtc-x1205.
config RTC_DRV_PALMAS
tristate "TI Palmas RTC driver"
depends on MFD_PALMAS
help
If you say yes here you get support for the RTC of TI PALMA series PMIC
chips.
This driver can also be built as a module. If so, the module
will be called rtc-palma.
config RTC_DRV_PCF8523
tristate "NXP PCF8523"
help
......
......@@ -81,6 +81,7 @@ obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o
obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
......
/*
* rtc-palmas.c -- Palmas Real Time Clock driver.
* RTC driver for TI Palma series devices like TPS65913,
* TPS65914 power management IC.
*
* Copyright (c) 2012, NVIDIA Corporation.
*
* Author: Laxman Dewangan <ldewangan@nvidia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
* whether express or implied; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include <linux/bcd.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/palmas.h>
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
struct palmas_rtc {
struct rtc_device *rtc;
struct device *dev;
unsigned int irq;
};
/* Total number of RTC registers needed to set time*/
#define PALMAS_NUM_TIME_REGS (PALMAS_YEARS_REG - PALMAS_SECONDS_REG + 1)
static int palmas_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
unsigned char rtc_data[PALMAS_NUM_TIME_REGS];
struct palmas *palmas = dev_get_drvdata(dev->parent);
int ret;
/* Copy RTC counting registers to static registers or latches */
ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
PALMAS_RTC_CTRL_REG_GET_TIME, PALMAS_RTC_CTRL_REG_GET_TIME);
if (ret < 0) {
dev_err(dev, "RTC CTRL reg update failed, err: %d\n", ret);
return ret;
}
ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG,
rtc_data, PALMAS_NUM_TIME_REGS);
if (ret < 0) {
dev_err(dev, "RTC_SECONDS reg read failed, err = %d\n", ret);
return ret;
}
tm->tm_sec = bcd2bin(rtc_data[0]);
tm->tm_min = bcd2bin(rtc_data[1]);
tm->tm_hour = bcd2bin(rtc_data[2]);
tm->tm_mday = bcd2bin(rtc_data[3]);
tm->tm_mon = bcd2bin(rtc_data[4]) - 1;
tm->tm_year = bcd2bin(rtc_data[5]) + 100;
return ret;
}
static int palmas_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
unsigned char rtc_data[PALMAS_NUM_TIME_REGS];
struct palmas *palmas = dev_get_drvdata(dev->parent);
int ret;
rtc_data[0] = bin2bcd(tm->tm_sec);
rtc_data[1] = bin2bcd(tm->tm_min);
rtc_data[2] = bin2bcd(tm->tm_hour);
rtc_data[3] = bin2bcd(tm->tm_mday);
rtc_data[4] = bin2bcd(tm->tm_mon + 1);
rtc_data[5] = bin2bcd(tm->tm_year - 100);
/* Stop RTC while updating the RTC time registers */
ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
PALMAS_RTC_CTRL_REG_STOP_RTC, 0);
if (ret < 0) {
dev_err(dev, "RTC stop failed, err = %d\n", ret);
return ret;
}
ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG,
rtc_data, PALMAS_NUM_TIME_REGS);
if (ret < 0) {
dev_err(dev, "RTC_SECONDS reg write failed, err = %d\n", ret);
return ret;
}
/* Start back RTC */
ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
PALMAS_RTC_CTRL_REG_STOP_RTC, PALMAS_RTC_CTRL_REG_STOP_RTC);
if (ret < 0)
dev_err(dev, "RTC start failed, err = %d\n", ret);
return ret;
}
static int palmas_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
{
struct palmas *palmas = dev_get_drvdata(dev->parent);
u8 val;
val = enabled ? PALMAS_RTC_INTERRUPTS_REG_IT_ALARM : 0;
return palmas_write(palmas, PALMAS_RTC_BASE,
PALMAS_RTC_INTERRUPTS_REG, val);
}
static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
unsigned char alarm_data[PALMAS_NUM_TIME_REGS];
u32 int_val;
struct palmas *palmas = dev_get_drvdata(dev->parent);
int ret;
ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE,
PALMAS_ALARM_SECONDS_REG,
alarm_data, PALMAS_NUM_TIME_REGS);
if (ret < 0) {
dev_err(dev, "RTC_ALARM_SECONDS read failed, err = %d\n", ret);
return ret;
}
alm->time.tm_sec = bcd2bin(alarm_data[0]);
alm->time.tm_min = bcd2bin(alarm_data[1]);
alm->time.tm_hour = bcd2bin(alarm_data[2]);
alm->time.tm_mday = bcd2bin(alarm_data[3]);
alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1;
alm->time.tm_year = bcd2bin(alarm_data[5]) + 100;
ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG,
&int_val);
if (ret < 0) {
dev_err(dev, "RTC_INTERRUPTS reg read failed, err = %d\n", ret);
return ret;
}
if (int_val & PALMAS_RTC_INTERRUPTS_REG_IT_ALARM)
alm->enabled = 1;
return ret;
}
static int palmas_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
unsigned char alarm_data[PALMAS_NUM_TIME_REGS];
struct palmas *palmas = dev_get_drvdata(dev->parent);
int ret;
ret = palmas_rtc_alarm_irq_enable(dev, 0);
if (ret < 0) {
dev_err(dev, "Disable RTC alarm failed\n");
return ret;
}
alarm_data[0] = bin2bcd(alm->time.tm_sec);
alarm_data[1] = bin2bcd(alm->time.tm_min);
alarm_data[2] = bin2bcd(alm->time.tm_hour);
alarm_data[3] = bin2bcd(alm->time.tm_mday);
alarm_data[4] = bin2bcd(alm->time.tm_mon + 1);
alarm_data[5] = bin2bcd(alm->time.tm_year - 100);
ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE,
PALMAS_ALARM_SECONDS_REG, alarm_data, PALMAS_NUM_TIME_REGS);
if (ret < 0) {
dev_err(dev, "ALARM_SECONDS_REG write failed, err = %d\n", ret);
return ret;
}
if (alm->enabled)
ret = palmas_rtc_alarm_irq_enable(dev, 1);
return ret;
}
static int palmas_clear_interrupts(struct device *dev)
{
struct palmas *palmas = dev_get_drvdata(dev->parent);
unsigned int rtc_reg;
int ret;
ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG,
&rtc_reg);
if (ret < 0) {
dev_err(dev, "RTC_STATUS read failed, err = %d\n", ret);
return ret;
}
ret = palmas_write(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG,
rtc_reg);
if (ret < 0) {
dev_err(dev, "RTC_STATUS write failed, err = %d\n", ret);
return ret;
}
return 0;
}
static irqreturn_t palmas_rtc_interrupt(int irq, void *context)
{
struct palmas_rtc *palmas_rtc = context;
struct device *dev = palmas_rtc->dev;
int ret;
ret = palmas_clear_interrupts(dev);
if (ret < 0) {
dev_err(dev, "RTC interrupt clear failed, err = %d\n", ret);
return IRQ_NONE;
}
rtc_update_irq(palmas_rtc->rtc, 1, RTC_IRQF | RTC_AF);
return IRQ_HANDLED;
}
static struct rtc_class_ops palmas_rtc_ops = {
.read_time = palmas_rtc_read_time,
.set_time = palmas_rtc_set_time,
.read_alarm = palmas_rtc_read_alarm,
.set_alarm = palmas_rtc_set_alarm,
.alarm_irq_enable = palmas_rtc_alarm_irq_enable,
};
static int palmas_rtc_probe(struct platform_device *pdev)
{
struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
struct palmas_rtc *palmas_rtc = NULL;
int ret;
palmas_rtc = devm_kzalloc(&pdev->dev, sizeof(struct palmas_rtc),
GFP_KERNEL);
if (!palmas_rtc)
return -ENOMEM;
/* Clear pending interrupts */
ret = palmas_clear_interrupts(&pdev->dev);
if (ret < 0) {
dev_err(&pdev->dev, "clear RTC int failed, err = %d\n", ret);
return ret;
}
palmas_rtc->dev = &pdev->dev;
platform_set_drvdata(pdev, palmas_rtc);
/* Start RTC */
ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
PALMAS_RTC_CTRL_REG_STOP_RTC,
PALMAS_RTC_CTRL_REG_STOP_RTC);
if (ret < 0) {
dev_err(&pdev->dev, "RTC_CTRL write failed, err = %d\n", ret);
return ret;
}
palmas_rtc->irq = platform_get_irq(pdev, 0);
palmas_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
&palmas_rtc_ops, THIS_MODULE);
if (IS_ERR(palmas_rtc->rtc)) {
ret = PTR_ERR(palmas_rtc->rtc);
dev_err(&pdev->dev, "RTC register failed, err = %d\n", ret);
return ret;
}
ret = request_threaded_irq(palmas_rtc->irq, NULL,
palmas_rtc_interrupt,
IRQF_TRIGGER_LOW | IRQF_ONESHOT |
IRQF_EARLY_RESUME,
dev_name(&pdev->dev), palmas_rtc);
if (ret < 0) {
dev_err(&pdev->dev, "IRQ request failed, err = %d\n", ret);
rtc_device_unregister(palmas_rtc->rtc);
return ret;
}
device_set_wakeup_capable(&pdev->dev, 1);
return 0;
}
static int palmas_rtc_remove(struct platform_device *pdev)
{
struct palmas_rtc *palmas_rtc = platform_get_drvdata(pdev);
palmas_rtc_alarm_irq_enable(&pdev->dev, 0);
free_irq(palmas_rtc->irq, palmas_rtc);
rtc_device_unregister(palmas_rtc->rtc);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int palmas_rtc_suspend(struct device *dev)
{
struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
enable_irq_wake(palmas_rtc->irq);
return 0;
}
static int palmas_rtc_resume(struct device *dev)
{
struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
disable_irq_wake(palmas_rtc->irq);
return 0;
}
#endif
static const struct dev_pm_ops palmas_rtc_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(palmas_rtc_suspend, palmas_rtc_resume)
};
static struct platform_driver palmas_rtc_driver = {
.probe = palmas_rtc_probe,
.remove = palmas_rtc_remove,
.driver = {
.owner = THIS_MODULE,
.name = "palmas-rtc",
.pm = &palmas_rtc_pm_ops,
},
};
module_platform_driver(palmas_rtc_driver);
MODULE_ALIAS("platform:palmas_rtc");
MODULE_DESCRIPTION("TI PALMAS series RTC driver");
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_LICENSE("GPL v2");
......@@ -107,7 +107,7 @@ static int omap_ehci_init(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int rc;
struct ehci_hcd_omap_platform_data *pdata;
struct usbhs_omap_platform_data *pdata;
pdata = hcd->self.controller->platform_data;
......@@ -151,7 +151,7 @@ static int omap_ehci_init(struct usb_hcd *hcd)
}
static void disable_put_regulator(
struct ehci_hcd_omap_platform_data *pdata)
struct usbhs_omap_platform_data *pdata)
{
int i;
......@@ -176,7 +176,7 @@ static void disable_put_regulator(
static int ehci_hcd_omap_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ehci_hcd_omap_platform_data *pdata = dev->platform_data;
struct usbhs_omap_platform_data *pdata = dev->platform_data;
struct resource *res;
struct usb_hcd *hcd;
void __iomem *regs;
......
......@@ -101,6 +101,29 @@ static const struct backlight_ops max8925_backlight_ops = {
.get_brightness = max8925_backlight_get_brightness,
};
#ifdef CONFIG_OF
static int max8925_backlight_dt_init(struct platform_device *pdev,
struct max8925_backlight_pdata *pdata)
{
struct device_node *nproot = pdev->dev.parent->of_node, *np;
int dual_string;
if (!nproot)
return -ENODEV;
np = of_find_node_by_name(nproot, "backlight");
if (!np) {
dev_err(&pdev->dev, "failed to find backlight node\n");
return -ENODEV;
}
of_property_read_u32(np, "maxim,max8925-dual-string", &dual_string);
pdata->dual_string = dual_string;
return 0;
}
#else
#define max8925_backlight_dt_init(x, y) (-1)
#endif
static int max8925_backlight_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
......@@ -147,6 +170,13 @@ static int max8925_backlight_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bl);
value = 0;
if (pdev->dev.parent->of_node && !pdata) {
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct max8925_backlight_pdata),
GFP_KERNEL);
max8925_backlight_dt_init(pdev, pdata);
}
if (pdata) {
if (pdata->lxw_scl)
value |= (1 << 7);
......@@ -158,7 +188,6 @@ static int max8925_backlight_probe(struct platform_device *pdev)
ret = max8925_set_bits(chip->i2c, data->reg_mode_cntl, 0xfe, value);
if (ret < 0)
goto out_brt;
backlight_update_status(bl);
return 0;
out_brt:
......
......@@ -364,6 +364,18 @@ config IMX2_WDT
To compile this driver as a module, choose M here: the
module will be called imx2_wdt.
config UX500_WATCHDOG
tristate "ST-Ericsson Ux500 watchdog"
depends on MFD_DB8500_PRCMU
select WATCHDOG_CORE
default y
help
Say Y here to include Watchdog timer support for the watchdog
existing in the prcmu of ST-Ericsson Ux500 series platforms.
To compile this driver as a module, choose M here: the
module will be called ux500_wdt.
# AVR32 Architecture
config AT32AP700X_WDT
......
......@@ -52,6 +52,7 @@ obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
......
/*
* Copyright (C) ST-Ericsson SA 2011-2013
*
* License Terms: GNU General Public License v2
*
* Author: Mathieu Poirier <mathieu.poirier@linaro.org> for ST-Ericsson
* Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/miscdevice.h>
#include <linux/err.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
#include <linux/platform_device.h>
#include <linux/platform_data/ux500_wdt.h>
#include <linux/mfd/dbx500-prcmu.h>
#define WATCHDOG_TIMEOUT 600 /* 10 minutes */
#define WATCHDOG_MIN 0
#define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */
#define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */
static unsigned int timeout = WATCHDOG_TIMEOUT;
module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static int ux500_wdt_start(struct watchdog_device *wdd)
{
return prcmu_enable_a9wdog(PRCMU_WDOG_ALL);
}
static int ux500_wdt_stop(struct watchdog_device *wdd)
{
return prcmu_disable_a9wdog(PRCMU_WDOG_ALL);
}
static int ux500_wdt_keepalive(struct watchdog_device *wdd)
{
return prcmu_kick_a9wdog(PRCMU_WDOG_ALL);
}
static int ux500_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
ux500_wdt_stop(wdd);
prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
ux500_wdt_start(wdd);
return 0;
}
static const struct watchdog_info ux500_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "Ux500 WDT",
.firmware_version = 1,
};
static const struct watchdog_ops ux500_wdt_ops = {
.owner = THIS_MODULE,
.start = ux500_wdt_start,
.stop = ux500_wdt_stop,
.ping = ux500_wdt_keepalive,
.set_timeout = ux500_wdt_set_timeout,
};
static struct watchdog_device ux500_wdt = {
.info = &ux500_wdt_info,
.ops = &ux500_wdt_ops,
.min_timeout = WATCHDOG_MIN,
.max_timeout = WATCHDOG_MAX32,
};
static int ux500_wdt_probe(struct platform_device *pdev)
{
int ret;
struct ux500_wdt_data *pdata = pdev->dev.platform_data;
if (pdata) {
if (pdata->timeout > 0)
timeout = pdata->timeout;
if (pdata->has_28_bits_resolution)
ux500_wdt.max_timeout = WATCHDOG_MAX28;
}
watchdog_set_nowayout(&ux500_wdt, nowayout);
/* disable auto off on sleep */
prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false);
/* set HW initial value */
prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
ret = watchdog_register_device(&ux500_wdt);
if (ret)
return ret;
dev_info(&pdev->dev, "initialized\n");
return 0;
}
static int ux500_wdt_remove(struct platform_device *dev)
{
watchdog_unregister_device(&ux500_wdt);
return 0;
}
#ifdef CONFIG_PM
static int ux500_wdt_suspend(struct platform_device *pdev,
pm_message_t state)
{
if (watchdog_active(&ux500_wdt)) {
ux500_wdt_stop(&ux500_wdt);
prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true);
prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
ux500_wdt_start(&ux500_wdt);
}
return 0;
}
static int ux500_wdt_resume(struct platform_device *pdev)
{
if (watchdog_active(&ux500_wdt)) {
ux500_wdt_stop(&ux500_wdt);
prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false);
prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
ux500_wdt_start(&ux500_wdt);
}
return 0;
}
#else
#define ux500_wdt_suspend NULL
#define ux500_wdt_resume NULL
#endif
static struct platform_driver ux500_wdt_driver = {
.probe = ux500_wdt_probe,
.remove = ux500_wdt_remove,
.suspend = ux500_wdt_suspend,
.resume = ux500_wdt_resume,
.driver = {
.owner = THIS_MODULE,
.name = "ux500_wdt",
},
};
module_platform_driver(ux500_wdt_driver);
MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>");
MODULE_DESCRIPTION("Ux500 Watchdog Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:ux500_wdt");
......@@ -39,51 +39,55 @@
* address each module uses within a given i2c slave.
*/
/* Module IDs for similar functionalities found in twl4030/twl6030 */
enum twl_module_ids {
TWL_MODULE_USB,
TWL_MODULE_PIH,
TWL_MODULE_MAIN_CHARGE,
TWL_MODULE_PM_MASTER,
TWL_MODULE_PM_RECEIVER,
TWL_MODULE_RTC,
TWL_MODULE_PWM,
TWL_MODULE_LED,
TWL_MODULE_SECURED_REG,
TWL_MODULE_LAST,
};
/* Modules only available in twl4030 series */
enum twl4030_module_ids {
TWL4030_MODULE_USB = 0, /* Slave 0 (i2c address 0x48) */
TWL4030_MODULE_AUDIO_VOICE, /* Slave 1 (i2c address 0x49) */
TWL4030_MODULE_AUDIO_VOICE = TWL_MODULE_LAST,
TWL4030_MODULE_GPIO,
TWL4030_MODULE_INTBR,
TWL4030_MODULE_PIH,
TWL4030_MODULE_TEST,
TWL4030_MODULE_KEYPAD, /* Slave 2 (i2c address 0x4a) */
TWL4030_MODULE_KEYPAD,
TWL4030_MODULE_MADC,
TWL4030_MODULE_INTERRUPTS,
TWL4030_MODULE_LED,
TWL4030_MODULE_MAIN_CHARGE,
TWL4030_MODULE_PRECHARGE,
TWL4030_MODULE_PWM0,
TWL4030_MODULE_PWM1,
TWL4030_MODULE_PWMA,
TWL4030_MODULE_BACKUP,
TWL4030_MODULE_INT,
TWL4030_MODULE_PWMB,
TWL5031_MODULE_ACCESSORY,
TWL5031_MODULE_INTERRUPTS,
TWL4030_MODULE_BACKUP, /* Slave 3 (i2c address 0x4b) */
TWL4030_MODULE_INT,
TWL4030_MODULE_PM_MASTER,
TWL4030_MODULE_PM_RECEIVER,
TWL4030_MODULE_RTC,
TWL4030_MODULE_SECURED_REG,
TWL4030_MODULE_LAST,
};
/* Similar functionalities implemented in TWL4030/6030 */
#define TWL_MODULE_USB TWL4030_MODULE_USB
#define TWL_MODULE_PIH TWL4030_MODULE_PIH
#define TWL_MODULE_MAIN_CHARGE TWL4030_MODULE_MAIN_CHARGE
#define TWL_MODULE_PM_MASTER TWL4030_MODULE_PM_MASTER
#define TWL_MODULE_PM_RECEIVER TWL4030_MODULE_PM_RECEIVER
#define TWL_MODULE_RTC TWL4030_MODULE_RTC
#define TWL_MODULE_PWM TWL4030_MODULE_PWM0
#define TWL_MODULE_LED TWL4030_MODULE_LED
/* Modules only available in twl6030 series */
enum twl6030_module_ids {
TWL6030_MODULE_ID0 = TWL_MODULE_LAST,
TWL6030_MODULE_ID1,
TWL6030_MODULE_ID2,
TWL6030_MODULE_GPADC,
TWL6030_MODULE_GASGAUGE,
TWL6030_MODULE_LAST,
};
#define TWL6030_MODULE_ID0 13
#define TWL6030_MODULE_ID1 14
#define TWL6030_MODULE_ID2 15
/* Until the clients has been converted to use TWL_MODULE_LED */
#define TWL4030_MODULE_LED TWL_MODULE_LED
#define GPIO_INTR_OFFSET 0
#define KEYPAD_INTR_OFFSET 1
......@@ -170,21 +174,23 @@ static inline int twl_class_is_ ##class(void) \
TWL_CLASS_IS(4030, TWL4030_CLASS_ID)
TWL_CLASS_IS(6030, TWL6030_CLASS_ID)
/*
* Read and write single 8-bit registers
*/
int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg);
int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg);
/*
* Read and write several 8-bit registers at once.
*
* IMPORTANT: For twl_i2c_write(), allocate num_bytes + 1
* for the value, and populate your data starting at offset 1.
*/
int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
/*
* Read and write single 8-bit registers
*/
static inline int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg) {
return twl_i2c_write(mod_no, &val, reg, 1);
}
static inline int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg) {
return twl_i2c_read(mod_no, val, reg, 1);
}
int twl_get_type(void);
int twl_get_version(void);
int twl_get_hfclk_rate(void);
......
......@@ -365,5 +365,5 @@ static inline int pm80x_dev_resume(struct device *dev)
extern int pm80x_init(struct i2c_client *client,
const struct i2c_device_id *id);
extern int pm80x_deinit(struct i2c_client *client);
extern int pm80x_deinit(void);
#endif /* __LINUX_MFD_88PM80X_H */
......@@ -311,6 +311,7 @@ int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank,
int abx500_get_chip_id(struct device *dev);
int abx500_event_registers_startup_state_get(struct device *dev, u8 *event);
int abx500_startup_irq_enabled(struct device *dev, unsigned int irq);
void abx500_dump_all_banks(void);
struct abx500_ops {
int (*get_chip_id) (struct device *);
......@@ -321,6 +322,7 @@ struct abx500_ops {
int (*mask_and_set_register) (struct device *, u8, u8, u8, u8);
int (*event_registers_startup_state_get) (struct device *, u8 *);
int (*startup_irq_enabled) (struct device *, unsigned int);
void (*dump_all_banks) (struct device *);
};
int abx500_register_ops(struct device *core_dev, struct abx500_ops *ops);
......
......@@ -37,6 +37,11 @@ static inline int ab8500_sysctrl_clear(u16 reg, u8 bits)
return ab8500_sysctrl_write(reg, bits, 0);
}
/* Configuration data for SysClkReq1RfClkBuf - SysClkReq8RfClkBuf */
struct ab8500_sysctrl_platform_data {
u8 initial_req_buf_config[8];
};
/* Registers */
#define AB8500_TURNONSTATUS 0x100
#define AB8500_RESETSTATUS 0x101
......
......@@ -368,10 +368,12 @@ struct regulator_reg_init;
struct regulator_init_data;
struct ab8500_gpio_platform_data;
struct ab8500_codec_platform_data;
struct ab8500_sysctrl_platform_data;
/**
* struct ab8500_platform_data - AB8500 platform data
* @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used
* @pm_power_off: Should machine pm power off hook be registered or not
* @init: board-specific initialization after detection of ab8500
* @num_regulator_reg_init: number of regulator init registers
* @regulator_reg_init: regulator init registers
......@@ -380,6 +382,7 @@ struct ab8500_codec_platform_data;
*/
struct ab8500_platform_data {
int irq_base;
bool pm_power_off;
void (*init) (struct ab8500 *);
int num_regulator_reg_init;
struct ab8500_regulator_reg_init *regulator_reg_init;
......@@ -387,6 +390,7 @@ struct ab8500_platform_data {
struct regulator_init_data *regulator;
struct abx500_gpio_platform_data *gpio;
struct ab8500_codec_platform_data *codec;
struct ab8500_sysctrl_platform_data *sysctrl;
};
extern int ab8500_init(struct ab8500 *ab8500,
......@@ -508,4 +512,12 @@ static inline int is_ab9540_2p0_or_earlier(struct ab8500 *ab)
return (is_ab9540(ab) && (ab->chip_id < AB8500_CUT2P0));
}
#ifdef CONFIG_AB8500_DEBUG
void ab8500_dump_all_banks(struct device *dev);
void ab8500_debug_register_interrupt(int line);
#else
static inline void ab8500_dump_all_banks(struct device *dev) {}
static inline void ab8500_debug_register_interrupt(int line) {}
#endif
#endif /* MFD_AB8500_H */
......@@ -56,6 +56,8 @@
#define ARIZONA_DMIC_MICBIAS2 2
#define ARIZONA_DMIC_MICBIAS3 3
#define ARIZONA_MAX_MICBIAS 3
#define ARIZONA_INMODE_DIFF 0
#define ARIZONA_INMODE_SE 1
#define ARIZONA_INMODE_DMIC 2
......@@ -71,6 +73,13 @@
struct regulator_init_data;
struct arizona_micbias {
int mV; /** Regulated voltage */
unsigned int ext_cap:1; /** External capacitor fitted */
unsigned int discharge:1; /** Actively discharge */
unsigned int fast_start:1; /** Enable aggressive startup ramp rate */
};
struct arizona_micd_config {
unsigned int src;
unsigned int bias;
......@@ -136,6 +145,9 @@ struct arizona_pdata {
/** Reference voltage for DMIC inputs */
int dmic_ref[ARIZONA_MAX_INPUT];
/** MICBIAS configurations */
struct arizona_micbias micbias[ARIZONA_MAX_MICBIAS];
/** Mode of input structures */
int inmode[ARIZONA_MAX_INPUT];
......
......@@ -984,18 +984,34 @@
#define ARIZONA_DSP1_STATUS_1 0x1104
#define ARIZONA_DSP1_STATUS_2 0x1105
#define ARIZONA_DSP1_STATUS_3 0x1106
#define ARIZONA_DSP1_SCRATCH_0 0x1140
#define ARIZONA_DSP1_SCRATCH_1 0x1141
#define ARIZONA_DSP1_SCRATCH_2 0x1142
#define ARIZONA_DSP1_SCRATCH_3 0x1143
#define ARIZONA_DSP2_CONTROL_1 0x1200
#define ARIZONA_DSP2_CLOCKING_1 0x1201
#define ARIZONA_DSP2_STATUS_1 0x1204
#define ARIZONA_DSP2_STATUS_2 0x1205
#define ARIZONA_DSP2_SCRATCH_0 0x1240
#define ARIZONA_DSP2_SCRATCH_1 0x1241
#define ARIZONA_DSP2_SCRATCH_2 0x1242
#define ARIZONA_DSP2_SCRATCH_3 0x1243
#define ARIZONA_DSP3_CONTROL_1 0x1300
#define ARIZONA_DSP3_CLOCKING_1 0x1301
#define ARIZONA_DSP3_STATUS_1 0x1304
#define ARIZONA_DSP3_STATUS_2 0x1305
#define ARIZONA_DSP3_SCRATCH_0 0x1340
#define ARIZONA_DSP3_SCRATCH_1 0x1341
#define ARIZONA_DSP3_SCRATCH_2 0x1342
#define ARIZONA_DSP3_SCRATCH_3 0x1343
#define ARIZONA_DSP4_CONTROL_1 0x1400
#define ARIZONA_DSP4_CLOCKING_1 0x1401
#define ARIZONA_DSP4_STATUS_1 0x1404
#define ARIZONA_DSP4_STATUS_2 0x1405
#define ARIZONA_DSP4_SCRATCH_0 0x1440
#define ARIZONA_DSP4_SCRATCH_1 0x1441
#define ARIZONA_DSP4_SCRATCH_2 0x1442
#define ARIZONA_DSP4_SCRATCH_3 0x1443
/*
* Field Definitions.
......
......@@ -150,6 +150,18 @@ enum prcmu_clock {
PRCMU_DSI2ESCCLK,
};
/**
* enum prcmu_wdog_id - PRCMU watchdog IDs
* @PRCMU_WDOG_ALL: use all timers
* @PRCMU_WDOG_CPU1: use first CPU timer only
* @PRCMU_WDOG_CPU2: use second CPU timer conly
*/
enum prcmu_wdog_id {
PRCMU_WDOG_ALL = 0x00,
PRCMU_WDOG_CPU1 = 0x01,
PRCMU_WDOG_CPU2 = 0x02,
};
/**
* enum ape_opp - APE OPP states definition
* @APE_OPP_INIT:
......
......@@ -190,6 +190,8 @@ enum {
MAX8925_NR_IRQS,
};
struct max8925_chip {
struct device *dev;
struct i2c_client *i2c;
......@@ -201,7 +203,6 @@ struct max8925_chip {
int irq_base;
int core_irq;
int tsc_irq;
unsigned int wakeup_flag;
};
......
......@@ -2789,4 +2789,56 @@ enum usb_irq_events {
#define PALMAS_GPADC_TRIM15 0xE
#define PALMAS_GPADC_TRIM16 0xF
static inline int palmas_read(struct palmas *palmas, unsigned int base,
unsigned int reg, unsigned int *val)
{
unsigned int addr = PALMAS_BASE_TO_REG(base, reg);
int slave_id = PALMAS_BASE_TO_SLAVE(base);
return regmap_read(palmas->regmap[slave_id], addr, val);
}
static inline int palmas_write(struct palmas *palmas, unsigned int base,
unsigned int reg, unsigned int value)
{
unsigned int addr = PALMAS_BASE_TO_REG(base, reg);
int slave_id = PALMAS_BASE_TO_SLAVE(base);
return regmap_write(palmas->regmap[slave_id], addr, value);
}
static inline int palmas_bulk_write(struct palmas *palmas, unsigned int base,
unsigned int reg, const void *val, size_t val_count)
{
unsigned int addr = PALMAS_BASE_TO_REG(base, reg);
int slave_id = PALMAS_BASE_TO_SLAVE(base);
return regmap_bulk_write(palmas->regmap[slave_id], addr,
val, val_count);
}
static inline int palmas_bulk_read(struct palmas *palmas, unsigned int base,
unsigned int reg, void *val, size_t val_count)
{
unsigned int addr = PALMAS_BASE_TO_REG(base, reg);
int slave_id = PALMAS_BASE_TO_SLAVE(base);
return regmap_bulk_read(palmas->regmap[slave_id], addr,
val, val_count);
}
static inline int palmas_update_bits(struct palmas *palmas, unsigned int base,
unsigned int reg, unsigned int mask, unsigned int val)
{
unsigned int addr = PALMAS_BASE_TO_REG(base, reg);
int slave_id = PALMAS_BASE_TO_SLAVE(base);
return regmap_update_bits(palmas->regmap[slave_id], addr, mask, val);
}
static inline int palmas_irq_get_virq(struct palmas *palmas, int irq)
{
return regmap_irq_get_virq(palmas->irq_data, irq);
}
#endif /* __LINUX_MFD_PALMAS_H */
......@@ -465,7 +465,7 @@
#define SD_RSP_TYPE_R6 0x01
#define SD_RSP_TYPE_R7 0x01
/* SD_CONFIURE3 */
/* SD_CONFIGURE3 */
#define SD_RSP_80CLK_TIMEOUT_EN 0x01
/* Card Transfer Reset Register */
......@@ -581,8 +581,11 @@
#define CARD_GPIO_DIR 0xFD57
#define CARD_GPIO 0xFD58
#define CARD_DATA_SOURCE 0xFD5B
#define SD30_CLK_DRIVE_SEL 0xFD5A
#define CARD_SELECT 0xFD5C
#define SD30_DRIVE_SEL 0xFD5E
#define SD30_CMD_DRIVE_SEL 0xFD5E
#define SD30_DAT_DRIVE_SEL 0xFD5F
#define CARD_CLK_EN 0xFD69
#define SDIO_CTRL 0xFD6B
#define CD_PAD_CTL 0xFD73
......@@ -655,6 +658,8 @@
#define MSGTXDATA3 0xFE47
#define MSGTXCTL 0xFE48
#define PETXCFG 0xFE49
#define LTR_CTL 0xFE4A
#define OBFF_CFG 0xFE4C
#define CDRESUMECTL 0xFE52
#define WAKE_SEL_CTL 0xFE54
......@@ -735,6 +740,7 @@ struct rtsx_pcr {
unsigned int card_inserted;
unsigned int card_removed;
unsigned int card_exist;
struct delayed_work carddet_work;
struct delayed_work idle_work;
......@@ -799,6 +805,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk);
int rtsx_pci_card_power_on(struct rtsx_pcr *pcr, int card);
int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card);
int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card);
int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage);
unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr);
void rtsx_pci_complete_unfinished_transfer(struct rtsx_pcr *pcr);
......
......@@ -55,6 +55,7 @@ struct ohci_hcd_omap_platform_data {
};
struct usbhs_omap_platform_data {
int nports;
enum usbhs_omap_port_mode port_mode[OMAP3_HS_USB_PORTS];
int reset_gpio_port[OMAP3_HS_USB_PORTS];
struct regulator *regulator[OMAP3_HS_USB_PORTS];
......
/*
* Copyright (C) ST Ericsson SA 2011
*
* License Terms: GNU General Public License v2
*
* STE Ux500 Watchdog platform data
*/
#ifndef __UX500_WDT_H
#define __UX500_WDT_H
/**
* struct ux500_wdt_data
*/
struct ux500_wdt_data {
unsigned int timeout;
bool has_28_bits_resolution;
};
#endif /* __UX500_WDT_H */
......@@ -27,6 +27,14 @@
#define VEXPRESS_GPIO_MMC_CARDIN 0
#define VEXPRESS_GPIO_MMC_WPROT 1
#define VEXPRESS_GPIO_FLASH_WPn 2
#define VEXPRESS_GPIO_LED0 3
#define VEXPRESS_GPIO_LED1 4
#define VEXPRESS_GPIO_LED2 5
#define VEXPRESS_GPIO_LED3 6
#define VEXPRESS_GPIO_LED4 7
#define VEXPRESS_GPIO_LED5 8
#define VEXPRESS_GPIO_LED6 9
#define VEXPRESS_GPIO_LED7 10
#define VEXPRESS_RES_FUNC(_site, _func) \
{ \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment