Commit 00440bcd authored by Dave Stevenson's avatar Dave Stevenson Committed by Mark Brown

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: default avatarDave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: default avatarDetlev Casanova <detlev.casanova@collabora.com>
Link: https://lore.kernel.org/r/20220124220129.158891-4-detlev.casanova@collabora.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 5665eee7
...@@ -27,18 +27,28 @@ ...@@ -27,18 +27,28 @@
#define REG_POWERON 0x85 #define REG_POWERON 0x85
#define REG_PWM 0x86 #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 = { static const struct regmap_config attiny_regmap_config = {
.reg_bits = 8, .reg_bits = 8,
.val_bits = 8, .val_bits = 8,
.disable_locking = 1,
.max_register = REG_PWM, .max_register = REG_PWM,
.cache_type = REGCACHE_NONE, .cache_type = REGCACHE_NONE,
}; };
static int attiny_lcd_power_enable(struct regulator_dev *rdev) static int attiny_lcd_power_enable(struct regulator_dev *rdev)
{ {
struct mutex *lock = rdev_get_drvdata(rdev);
unsigned int data; unsigned int data;
int ret, i; int ret, i;
mutex_lock(lock);
regmap_write(rdev->regmap, REG_POWERON, 1); regmap_write(rdev->regmap, REG_POWERON, 1);
msleep(80); msleep(80);
...@@ -63,33 +73,49 @@ static int attiny_lcd_power_enable(struct regulator_dev *rdev) ...@@ -63,33 +73,49 @@ static int attiny_lcd_power_enable(struct regulator_dev *rdev)
*/ */
regmap_write(rdev->regmap, REG_PORTA, BIT(2)); regmap_write(rdev->regmap, REG_PORTA, BIT(2));
mutex_unlock(lock);
return 0; return 0;
} }
static int attiny_lcd_power_disable(struct regulator_dev *rdev) 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_PWM, 0);
regmap_write(rdev->regmap, REG_POWERON, 0); regmap_write(rdev->regmap, REG_POWERON, 0);
msleep(30); msleep(30);
mutex_unlock(lock);
return 0; return 0;
} }
static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev) static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev)
{ {
struct mutex *lock = rdev_get_drvdata(rdev);
unsigned int data; unsigned int data;
int ret, i; int ret, i;
mutex_lock(lock);
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
ret = regmap_read(rdev->regmap, REG_POWERON, &data); ret = regmap_read(rdev->regmap, REG_POWERON, &data);
if (!ret) if (!ret)
break; break;
usleep_range(10000, 12000); usleep_range(10000, 12000);
} }
if (ret < 0) if (ret < 0) {
mutex_unlock(lock);
return ret; return ret;
}
if (!(data & BIT(0))) if (!(data & BIT(0))) {
mutex_unlock(lock);
return 0; return 0;
}
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
ret = regmap_read(rdev->regmap, REG_PORTB, &data); ret = regmap_read(rdev->regmap, REG_PORTB, &data);
...@@ -98,6 +124,8 @@ static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev) ...@@ -98,6 +124,8 @@ static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev)
usleep_range(10000, 12000); usleep_range(10000, 12000);
} }
mutex_unlock(lock);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -125,10 +153,13 @@ static const struct regulator_desc attiny_regulator = { ...@@ -125,10 +153,13 @@ static const struct regulator_desc attiny_regulator = {
static int attiny_update_status(struct backlight_device *bl) 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 brightness = bl->props.brightness;
int ret, i; int ret, i;
mutex_lock(&state->lock);
if (bl->props.power != FB_BLANK_UNBLANK || if (bl->props.power != FB_BLANK_UNBLANK ||
bl->props.fb_blank != FB_BLANK_UNBLANK) bl->props.fb_blank != FB_BLANK_UNBLANK)
brightness = 0; brightness = 0;
...@@ -139,20 +170,27 @@ static int attiny_update_status(struct backlight_device *bl) ...@@ -139,20 +170,27 @@ static int attiny_update_status(struct backlight_device *bl)
break; break;
} }
mutex_unlock(&state->lock);
return ret; return ret;
} }
static int attiny_get_brightness(struct backlight_device *bl) 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; int ret, brightness, i;
mutex_lock(&state->lock);
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
ret = regmap_read(regmap, REG_PWM, &brightness); ret = regmap_read(regmap, REG_PWM, &brightness);
if (!ret) if (!ret)
break; break;
} }
mutex_unlock(&state->lock);
if (ret) if (ret)
return ret; return ret;
...@@ -174,22 +212,30 @@ static int attiny_i2c_probe(struct i2c_client *i2c, ...@@ -174,22 +212,30 @@ static int attiny_i2c_probe(struct i2c_client *i2c,
struct regulator_config config = { }; struct regulator_config config = { };
struct backlight_device *bl; struct backlight_device *bl;
struct regulator_dev *rdev; struct regulator_dev *rdev;
struct attiny_lcd *state;
struct regmap *regmap; struct regmap *regmap;
unsigned int data; unsigned int data;
int ret; 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); regmap = devm_regmap_init_i2c(i2c, &attiny_regmap_config);
if (IS_ERR(regmap)) { if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap); ret = PTR_ERR(regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n", dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret); ret);
return ret; goto error;
} }
ret = regmap_read(regmap, REG_ID, &data); ret = regmap_read(regmap, REG_ID, &data);
if (ret < 0) { if (ret < 0) {
dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret); dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret);
return ret; goto error;
} }
switch (data) { switch (data) {
...@@ -198,7 +244,8 @@ static int attiny_i2c_probe(struct i2c_client *i2c, ...@@ -198,7 +244,8 @@ static int attiny_i2c_probe(struct i2c_client *i2c,
break; break;
default: default:
dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x\n", data); dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x\n", data);
return -ENODEV; ret = -ENODEV;
goto error;
} }
regmap_write(regmap, REG_POWERON, 0); regmap_write(regmap, REG_POWERON, 0);
...@@ -208,23 +255,44 @@ static int attiny_i2c_probe(struct i2c_client *i2c, ...@@ -208,23 +255,44 @@ static int attiny_i2c_probe(struct i2c_client *i2c,
config.regmap = regmap; config.regmap = regmap;
config.of_node = i2c->dev.of_node; config.of_node = i2c->dev.of_node;
config.init_data = &attiny_regulator_default; config.init_data = &attiny_regulator_default;
config.driver_data = &state->lock;
rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config); rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config);
if (IS_ERR(rdev)) { if (IS_ERR(rdev)) {
dev_err(&i2c->dev, "Failed to register ATTINY regulator\n"); 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.type = BACKLIGHT_RAW;
props.max_brightness = 0xff; props.max_brightness = 0xff;
state->regmap = regmap;
bl = devm_backlight_device_register(&i2c->dev, dev_name(&i2c->dev), bl = devm_backlight_device_register(&i2c->dev, dev_name(&i2c->dev),
&i2c->dev, regmap, &attiny_bl, &i2c->dev, state, &attiny_bl,
&props); &props);
if (IS_ERR(bl)) if (IS_ERR(bl)) {
return PTR_ERR(bl); ret = PTR_ERR(bl);
goto error;
}
bl->props.brightness = 0xff; 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; return 0;
} }
...@@ -240,6 +308,7 @@ static struct i2c_driver attiny_regulator_driver = { ...@@ -240,6 +308,7 @@ static struct i2c_driver attiny_regulator_driver = {
.of_match_table = of_match_ptr(attiny_dt_ids), .of_match_table = of_match_ptr(attiny_dt_ids),
}, },
.probe = attiny_i2c_probe, .probe = attiny_i2c_probe,
.remove = attiny_i2c_remove,
}; };
module_i2c_driver(attiny_regulator_driver); module_i2c_driver(attiny_regulator_driver);
......
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