cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rpi-panel-attiny-regulator.c (9651B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2020 Marek Vasut <marex@denx.de>
      4 *
      5 * Based on rpi_touchscreen.c by Eric Anholt <eric@anholt.net>
      6 */
      7
      8#include <linux/backlight.h>
      9#include <linux/err.h>
     10#include <linux/gpio.h>
     11#include <linux/gpio/driver.h>
     12#include <linux/i2c.h>
     13#include <linux/init.h>
     14#include <linux/interrupt.h>
     15#include <linux/module.h>
     16#include <linux/regmap.h>
     17#include <linux/regulator/driver.h>
     18#include <linux/regulator/machine.h>
     19#include <linux/regulator/of_regulator.h>
     20#include <linux/slab.h>
     21
     22/* I2C registers of the Atmel microcontroller. */
     23#define REG_ID		0x80
     24#define REG_PORTA	0x81
     25#define REG_PORTB	0x82
     26#define REG_PORTC	0x83
     27#define REG_POWERON	0x85
     28#define REG_PWM		0x86
     29#define REG_ADDR_L	0x8c
     30#define REG_ADDR_H	0x8d
     31#define REG_WRITE_DATA_H	0x90
     32#define REG_WRITE_DATA_L	0x91
     33
     34#define PA_LCD_DITHB		BIT(0)
     35#define PA_LCD_MODE		BIT(1)
     36#define PA_LCD_LR		BIT(2)
     37#define PA_LCD_UD		BIT(3)
     38
     39#define PB_BRIDGE_PWRDNX_N	BIT(0)
     40#define PB_LCD_VCC_N		BIT(1)
     41#define PB_LCD_MAIN		BIT(7)
     42
     43#define PC_LED_EN		BIT(0)
     44#define PC_RST_TP_N		BIT(1)
     45#define PC_RST_LCD_N		BIT(2)
     46#define PC_RST_BRIDGE_N		BIT(3)
     47
     48enum gpio_signals {
     49	RST_BRIDGE_N,	/* TC358762 bridge reset */
     50	RST_TP_N,	/* Touch controller reset */
     51	NUM_GPIO
     52};
     53
     54struct gpio_signal_mappings {
     55	unsigned int reg;
     56	unsigned int mask;
     57};
     58
     59static const struct gpio_signal_mappings mappings[NUM_GPIO] = {
     60	[RST_BRIDGE_N] = { REG_PORTC, PC_RST_BRIDGE_N | PC_RST_LCD_N  },
     61	[RST_TP_N] = { REG_PORTC, PC_RST_TP_N },
     62};
     63
     64struct attiny_lcd {
     65	/* lock to serialise overall accesses to the Atmel */
     66	struct mutex	lock;
     67	struct regmap	*regmap;
     68	bool gpio_states[NUM_GPIO];
     69	u8 port_states[3];
     70
     71	struct gpio_chip gc;
     72};
     73
     74static const struct regmap_config attiny_regmap_config = {
     75	.reg_bits = 8,
     76	.val_bits = 8,
     77	.disable_locking = 1,
     78	.max_register = REG_WRITE_DATA_L,
     79	.cache_type = REGCACHE_RBTREE,
     80};
     81
     82static int attiny_set_port_state(struct attiny_lcd *state, int reg, u8 val)
     83{
     84	state->port_states[reg - REG_PORTA] = val;
     85	return regmap_write(state->regmap, reg, val);
     86};
     87
     88static u8 attiny_get_port_state(struct attiny_lcd *state, int reg)
     89{
     90	return state->port_states[reg - REG_PORTA];
     91};
     92
     93static int attiny_lcd_power_enable(struct regulator_dev *rdev)
     94{
     95	struct attiny_lcd *state = rdev_get_drvdata(rdev);
     96
     97	mutex_lock(&state->lock);
     98
     99	/* Ensure bridge, and tp stay in reset */
    100	attiny_set_port_state(state, REG_PORTC, 0);
    101	usleep_range(5000, 10000);
    102
    103	/* Default to the same orientation as the closed source
    104	 * firmware used for the panel.  Runtime rotation
    105	 * configuration will be supported using VC4's plane
    106	 * orientation bits.
    107	 */
    108	attiny_set_port_state(state, REG_PORTA, PA_LCD_LR);
    109	usleep_range(5000, 10000);
    110	/* Main regulator on, and power to the panel (LCD_VCC_N) */
    111	attiny_set_port_state(state, REG_PORTB, PB_LCD_MAIN);
    112	usleep_range(5000, 10000);
    113	/* Bring controllers out of reset */
    114	attiny_set_port_state(state, REG_PORTC, PC_LED_EN);
    115
    116	msleep(80);
    117
    118	mutex_unlock(&state->lock);
    119
    120	return 0;
    121}
    122
    123static int attiny_lcd_power_disable(struct regulator_dev *rdev)
    124{
    125	struct attiny_lcd *state = rdev_get_drvdata(rdev);
    126
    127	mutex_lock(&state->lock);
    128
    129	regmap_write(rdev->regmap, REG_PWM, 0);
    130	usleep_range(5000, 10000);
    131
    132	attiny_set_port_state(state, REG_PORTA, 0);
    133	usleep_range(5000, 10000);
    134	attiny_set_port_state(state, REG_PORTB, PB_LCD_VCC_N);
    135	usleep_range(5000, 10000);
    136	attiny_set_port_state(state, REG_PORTC, 0);
    137	msleep(30);
    138
    139	mutex_unlock(&state->lock);
    140
    141	return 0;
    142}
    143
    144static int attiny_lcd_power_is_enabled(struct regulator_dev *rdev)
    145{
    146	struct attiny_lcd *state = rdev_get_drvdata(rdev);
    147	unsigned int data;
    148	int ret, i;
    149
    150	mutex_lock(&state->lock);
    151
    152	for (i = 0; i < 10; i++) {
    153		ret = regmap_read(rdev->regmap, REG_PORTC, &data);
    154		if (!ret)
    155			break;
    156		usleep_range(10000, 12000);
    157	}
    158
    159	mutex_unlock(&state->lock);
    160
    161	if (ret < 0)
    162		return ret;
    163
    164	return data & PC_RST_BRIDGE_N;
    165}
    166
    167static const struct regulator_init_data attiny_regulator_default = {
    168	.constraints = {
    169		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
    170	},
    171};
    172
    173static const struct regulator_ops attiny_regulator_ops = {
    174	.enable = attiny_lcd_power_enable,
    175	.disable = attiny_lcd_power_disable,
    176	.is_enabled = attiny_lcd_power_is_enabled,
    177};
    178
    179static const struct regulator_desc attiny_regulator = {
    180	.name	= "tc358762-power",
    181	.ops	= &attiny_regulator_ops,
    182	.type	= REGULATOR_VOLTAGE,
    183	.owner	= THIS_MODULE,
    184};
    185
    186static int attiny_update_status(struct backlight_device *bl)
    187{
    188	struct attiny_lcd *state = bl_get_data(bl);
    189	struct regmap *regmap = state->regmap;
    190	int brightness = bl->props.brightness;
    191	int ret, i;
    192
    193	mutex_lock(&state->lock);
    194
    195	if (bl->props.power != FB_BLANK_UNBLANK ||
    196	    bl->props.fb_blank != FB_BLANK_UNBLANK)
    197		brightness = 0;
    198
    199	for (i = 0; i < 10; i++) {
    200		ret = regmap_write(regmap, REG_PWM, brightness);
    201		if (!ret)
    202			break;
    203	}
    204
    205	mutex_unlock(&state->lock);
    206
    207	return ret;
    208}
    209
    210static const struct backlight_ops attiny_bl = {
    211	.update_status	= attiny_update_status,
    212};
    213
    214static int attiny_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
    215{
    216	return GPIO_LINE_DIRECTION_OUT;
    217}
    218
    219static void attiny_gpio_set(struct gpio_chip *gc, unsigned int off, int val)
    220{
    221	struct attiny_lcd *state = gpiochip_get_data(gc);
    222	u8 last_val;
    223
    224	if (off >= NUM_GPIO)
    225		return;
    226
    227	mutex_lock(&state->lock);
    228
    229	last_val = attiny_get_port_state(state, mappings[off].reg);
    230	if (val)
    231		last_val |= mappings[off].mask;
    232	else
    233		last_val &= ~mappings[off].mask;
    234
    235	attiny_set_port_state(state, mappings[off].reg, last_val);
    236
    237	if (off == RST_BRIDGE_N && val) {
    238		usleep_range(5000, 8000);
    239		regmap_write(state->regmap, REG_ADDR_H, 0x04);
    240		usleep_range(5000, 8000);
    241		regmap_write(state->regmap, REG_ADDR_L, 0x7c);
    242		usleep_range(5000, 8000);
    243		regmap_write(state->regmap, REG_WRITE_DATA_H, 0x00);
    244		usleep_range(5000, 8000);
    245		regmap_write(state->regmap, REG_WRITE_DATA_L, 0x00);
    246
    247		msleep(100);
    248	}
    249
    250	mutex_unlock(&state->lock);
    251}
    252
    253static int attiny_i2c_read(struct i2c_client *client, u8 reg, unsigned int *buf)
    254{
    255	struct i2c_msg msgs[1];
    256	u8 addr_buf[1] = { reg };
    257	u8 data_buf[1] = { 0, };
    258	int ret;
    259
    260	/* Write register address */
    261	msgs[0].addr = client->addr;
    262	msgs[0].flags = 0;
    263	msgs[0].len = ARRAY_SIZE(addr_buf);
    264	msgs[0].buf = addr_buf;
    265
    266	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
    267	if (ret != ARRAY_SIZE(msgs))
    268		return -EIO;
    269
    270	usleep_range(5000, 10000);
    271
    272	/* Read data from register */
    273	msgs[0].addr = client->addr;
    274	msgs[0].flags = I2C_M_RD;
    275	msgs[0].len = 1;
    276	msgs[0].buf = data_buf;
    277
    278	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
    279	if (ret != ARRAY_SIZE(msgs))
    280		return -EIO;
    281
    282	*buf = data_buf[0];
    283	return 0;
    284}
    285
    286/*
    287 * I2C driver interface functions
    288 */
    289static int attiny_i2c_probe(struct i2c_client *i2c,
    290		const struct i2c_device_id *id)
    291{
    292	struct backlight_properties props = { };
    293	struct regulator_config config = { };
    294	struct backlight_device *bl;
    295	struct regulator_dev *rdev;
    296	struct attiny_lcd *state;
    297	struct regmap *regmap;
    298	unsigned int data;
    299	int ret;
    300
    301	state = devm_kzalloc(&i2c->dev, sizeof(*state), GFP_KERNEL);
    302	if (!state)
    303		return -ENOMEM;
    304
    305	mutex_init(&state->lock);
    306	i2c_set_clientdata(i2c, state);
    307
    308	regmap = devm_regmap_init_i2c(i2c, &attiny_regmap_config);
    309	if (IS_ERR(regmap)) {
    310		ret = PTR_ERR(regmap);
    311		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
    312			ret);
    313		goto error;
    314	}
    315
    316	ret = attiny_i2c_read(i2c, REG_ID, &data);
    317	if (ret < 0) {
    318		dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret);
    319		goto error;
    320	}
    321
    322	switch (data) {
    323	case 0xde: /* ver 1 */
    324	case 0xc3: /* ver 2 */
    325		break;
    326	default:
    327		dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x\n", data);
    328		ret = -ENODEV;
    329		goto error;
    330	}
    331
    332	regmap_write(regmap, REG_POWERON, 0);
    333	msleep(30);
    334	regmap_write(regmap, REG_PWM, 0);
    335
    336	config.dev = &i2c->dev;
    337	config.regmap = regmap;
    338	config.of_node = i2c->dev.of_node;
    339	config.init_data = &attiny_regulator_default;
    340	config.driver_data = state;
    341
    342	rdev = devm_regulator_register(&i2c->dev, &attiny_regulator, &config);
    343	if (IS_ERR(rdev)) {
    344		dev_err(&i2c->dev, "Failed to register ATTINY regulator\n");
    345		ret = PTR_ERR(rdev);
    346		goto error;
    347	}
    348
    349	props.type = BACKLIGHT_RAW;
    350	props.max_brightness = 0xff;
    351
    352	state->regmap = regmap;
    353
    354	bl = devm_backlight_device_register(&i2c->dev, dev_name(&i2c->dev),
    355					    &i2c->dev, state, &attiny_bl,
    356					    &props);
    357	if (IS_ERR(bl)) {
    358		ret = PTR_ERR(bl);
    359		goto error;
    360	}
    361
    362	bl->props.brightness = 0xff;
    363
    364	state->gc.parent = &i2c->dev;
    365	state->gc.label = i2c->name;
    366	state->gc.owner = THIS_MODULE;
    367	state->gc.base = -1;
    368	state->gc.ngpio = NUM_GPIO;
    369
    370	state->gc.set = attiny_gpio_set;
    371	state->gc.get_direction = attiny_gpio_get_direction;
    372	state->gc.can_sleep = true;
    373
    374	ret = devm_gpiochip_add_data(&i2c->dev, &state->gc, state);
    375	if (ret) {
    376		dev_err(&i2c->dev, "Failed to create gpiochip: %d\n", ret);
    377		goto error;
    378	}
    379
    380	return 0;
    381
    382error:
    383	mutex_destroy(&state->lock);
    384
    385	return ret;
    386}
    387
    388static int attiny_i2c_remove(struct i2c_client *client)
    389{
    390	struct attiny_lcd *state = i2c_get_clientdata(client);
    391
    392	mutex_destroy(&state->lock);
    393
    394	return 0;
    395}
    396
    397static const struct of_device_id attiny_dt_ids[] = {
    398	{ .compatible = "raspberrypi,7inch-touchscreen-panel-regulator" },
    399	{},
    400};
    401MODULE_DEVICE_TABLE(of, attiny_dt_ids);
    402
    403static struct i2c_driver attiny_regulator_driver = {
    404	.driver = {
    405		.name = "rpi_touchscreen_attiny",
    406		.of_match_table = of_match_ptr(attiny_dt_ids),
    407	},
    408	.probe = attiny_i2c_probe,
    409	.remove	= attiny_i2c_remove,
    410};
    411
    412module_i2c_driver(attiny_regulator_driver);
    413
    414MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
    415MODULE_DESCRIPTION("Regulator device driver for Raspberry Pi 7-inch touchscreen");
    416MODULE_LICENSE("GPL v2");