mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-30 18:47:06 +00:00
256 lines
5.9 KiB
Diff
256 lines
5.9 KiB
Diff
|
From 354dfe9793ff5f814830c6841ca07ef04bfd66e9 Mon Sep 17 00:00:00 2001
|
||
|
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
||
|
Date: Wed, 8 Sep 2021 15:02:05 +0100
|
||
|
Subject: [PATCH] regulator: rpi-panel: Serialise operations.
|
||
|
|
||
|
The driver was using the regmap lock to serialise the
|
||
|
individual accesses, but we really need to protect the
|
||
|
timings of enabling the regulators, including any communication
|
||
|
with the Atmel.
|
||
|
|
||
|
Use a mutex within the driver to control overall accesses to
|
||
|
the Atmel, instead of the regmap lock.
|
||
|
|
||
|
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
||
|
---
|
||
|
.../regulator/rpi-panel-attiny-regulator.c | 91 ++++++++++++++++---
|
||
|
1 file changed, 80 insertions(+), 11 deletions(-)
|
||
|
|
||
|
--- a/drivers/regulator/rpi-panel-attiny-regulator.c
|
||
|
+++ b/drivers/regulator/rpi-panel-attiny-regulator.c
|
||
|
@@ -27,18 +27,28 @@
|
||
|
#define REG_POWERON 0x85
|
||
|
#define REG_PWM 0x86
|
||
|
|
||
|
+struct attiny_lcd {
|
||
|
+ /* lock to serialise overall accesses to the Atmel */
|
||
|
+ struct mutex lock;
|
||
|
+ struct regmap *regmap;
|
||
|
+};
|
||
|
+
|
||
|
static const struct regmap_config attiny_regmap_config = {
|
||
|
.reg_bits = 8,
|
||
|
.val_bits = 8,
|
||
|
+ .disable_locking = 1,
|
||
|
.max_register = REG_PWM,
|
||
|
.cache_type = REGCACHE_NONE,
|
||
|
};
|
||
|
|
||
|
static int attiny_lcd_power_enable(struct regulator_dev *rdev)
|
||
|
{
|
||
|
+ struct mutex *lock = rdev_get_drvdata(rdev);
|
||
|
unsigned int data;
|
||
|
int ret, i;
|
||
|
|
||
|
+ mutex_lock(lock);
|
||
|
+
|
||
|
regmap_write(rdev->regmap, REG_POWERON, 1);
|
||
|
msleep(80);
|
||
|
|
||
|
@@ -63,33 +73,49 @@ static int attiny_lcd_power_enable(struc
|
||
|
*/
|
||
|
regmap_write(rdev->regmap, REG_PORTA, BIT(2));
|
||
|
|
||
|
+ mutex_unlock(lock);
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int attiny_lcd_power_disable(struct regulator_dev *rdev)
|
||
|
{
|
||
|
+ struct mutex *lock = rdev_get_drvdata(rdev);
|
||
|
+
|
||
|
+ mutex_lock(lock);
|
||
|
+
|
||
|
regmap_write(rdev->regmap, REG_PWM, 0);
|
||
|
regmap_write(rdev->regmap, REG_POWERON, 0);
|
||
|
msleep(30);
|
||
|
+
|
||
|
+ mutex_unlock(lock);
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev)
|
||
|
{
|
||
|
+ struct mutex *lock = rdev_get_drvdata(rdev);
|
||
|
unsigned int data;
|
||
|
int ret, i;
|
||
|
|
||
|
+ mutex_lock(lock);
|
||
|
+
|
||
|
for (i = 0; i < 10; i++) {
|
||
|
ret = regmap_read(rdev->regmap, REG_POWERON, &data);
|
||
|
if (!ret)
|
||
|
break;
|
||
|
usleep_range(10000, 12000);
|
||
|
}
|
||
|
- if (ret < 0)
|
||
|
+ if (ret < 0) {
|
||
|
+ mutex_unlock(lock);
|
||
|
return ret;
|
||
|
+ }
|
||
|
|
||
|
- if (!(data & BIT(0)))
|
||
|
+ if (!(data & BIT(0))) {
|
||
|
+ mutex_unlock(lock);
|
||
|
return 0;
|
||
|
+ }
|
||
|
|
||
|
for (i = 0; i < 10; i++) {
|
||
|
ret = regmap_read(rdev->regmap, REG_PORTB, &data);
|
||
|
@@ -98,6 +124,8 @@ static int attiny_lcd_power_is_enabled(s
|
||
|
usleep_range(10000, 12000);
|
||
|
}
|
||
|
|
||
|
+ mutex_unlock(lock);
|
||
|
+
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
@@ -125,10 +153,13 @@ static const struct regulator_desc attin
|
||
|
|
||
|
static int attiny_update_status(struct backlight_device *bl)
|
||
|
{
|
||
|
- struct regmap *regmap = bl_get_data(bl);
|
||
|
+ struct attiny_lcd *state = bl_get_data(bl);
|
||
|
+ struct regmap *regmap = state->regmap;
|
||
|
int brightness = bl->props.brightness;
|
||
|
int ret, i;
|
||
|
|
||
|
+ mutex_lock(&state->lock);
|
||
|
+
|
||
|
if (bl->props.power != FB_BLANK_UNBLANK ||
|
||
|
bl->props.fb_blank != FB_BLANK_UNBLANK)
|
||
|
brightness = 0;
|
||
|
@@ -139,20 +170,27 @@ static int attiny_update_status(struct b
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
+ mutex_unlock(&state->lock);
|
||
|
+
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int attiny_get_brightness(struct backlight_device *bl)
|
||
|
{
|
||
|
- struct regmap *regmap = bl_get_data(bl);
|
||
|
+ struct attiny_lcd *state = bl_get_data(bl);
|
||
|
+ struct regmap *regmap = state->regmap;
|
||
|
int ret, brightness, i;
|
||
|
|
||
|
+ mutex_lock(&state->lock);
|
||
|
+
|
||
|
for (i = 0; i < 10; i++) {
|
||
|
ret = regmap_read(regmap, REG_PWM, &brightness);
|
||
|
if (!ret)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
+ mutex_unlock(&state->lock);
|
||
|
+
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
@@ -174,22 +212,30 @@ static int attiny_i2c_probe(struct i2c_c
|
||
|
struct regulator_config config = { };
|
||
|
struct backlight_device *bl;
|
||
|
struct regulator_dev *rdev;
|
||
|
+ struct attiny_lcd *state;
|
||
|
struct regmap *regmap;
|
||
|
unsigned int data;
|
||
|
int ret;
|
||
|
|
||
|
+ state = devm_kzalloc(&i2c->dev, sizeof(*state), GFP_KERNEL);
|
||
|
+ if (!state)
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ mutex_init(&state->lock);
|
||
|
+ i2c_set_clientdata(i2c, state);
|
||
|
+
|
||
|
regmap = devm_regmap_init_i2c(i2c, &attiny_regmap_config);
|
||
|
if (IS_ERR(regmap)) {
|
||
|
ret = PTR_ERR(regmap);
|
||
|
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
||
|
ret);
|
||
|
- return ret;
|
||
|
+ goto error;
|
||
|
}
|
||
|
|
||
|
ret = regmap_read(regmap, REG_ID, &data);
|
||
|
if (ret < 0) {
|
||
|
dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret);
|
||
|
- return ret;
|
||
|
+ goto error;
|
||
|
}
|
||
|
|
||
|
switch (data) {
|
||
|
@@ -198,7 +244,8 @@ static int attiny_i2c_probe(struct i2c_c
|
||
|
break;
|
||
|
default:
|
||
|
dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x\n", data);
|
||
|
- return -ENODEV;
|
||
|
+ ret = -ENODEV;
|
||
|
+ goto error;
|
||
|
}
|
||
|
|
||
|
regmap_write(regmap, REG_POWERON, 0);
|
||
|
@@ -208,24 +255,45 @@ static int attiny_i2c_probe(struct i2c_c
|
||
|
config.regmap = regmap;
|
||
|
config.of_node = i2c->dev.of_node;
|
||
|
config.init_data = &attiny_regulator_default;
|
||
|
+ config.driver_data = &state->lock;
|
||
|
|
||
|
rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config);
|
||
|
if (IS_ERR(rdev)) {
|
||
|
dev_err(&i2c->dev, "Failed to register ATTINY regulator\n");
|
||
|
- return PTR_ERR(rdev);
|
||
|
+ ret = PTR_ERR(rdev);
|
||
|
+ goto error;
|
||
|
}
|
||
|
|
||
|
props.type = BACKLIGHT_RAW;
|
||
|
props.max_brightness = 0xff;
|
||
|
+
|
||
|
+ state->regmap = regmap;
|
||
|
+
|
||
|
bl = devm_backlight_device_register(&i2c->dev, dev_name(&i2c->dev),
|
||
|
- &i2c->dev, regmap, &attiny_bl,
|
||
|
+ &i2c->dev, state, &attiny_bl,
|
||
|
&props);
|
||
|
- if (IS_ERR(bl))
|
||
|
- return PTR_ERR(bl);
|
||
|
+ if (IS_ERR(bl)) {
|
||
|
+ ret = PTR_ERR(bl);
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
|
||
|
bl->props.brightness = 0xff;
|
||
|
|
||
|
return 0;
|
||
|
+
|
||
|
+error:
|
||
|
+ mutex_destroy(&state->lock);
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static int attiny_i2c_remove(struct i2c_client *client)
|
||
|
+{
|
||
|
+ struct attiny_lcd *state = i2c_get_clientdata(client);
|
||
|
+
|
||
|
+ mutex_destroy(&state->lock);
|
||
|
+
|
||
|
+ return 0;
|
||
|
}
|
||
|
|
||
|
static const struct of_device_id attiny_dt_ids[] = {
|
||
|
@@ -240,6 +308,7 @@ static struct i2c_driver attiny_regulato
|
||
|
.of_match_table = of_match_ptr(attiny_dt_ids),
|
||
|
},
|
||
|
.probe = attiny_i2c_probe,
|
||
|
+ .remove = attiny_i2c_remove,
|
||
|
};
|
||
|
|
||
|
module_i2c_driver(attiny_regulator_driver);
|