otgcontrol: rm-otgcontrol driver initial commit
parent
73eec6b12c
commit
5279a775da
|
@ -538,4 +538,5 @@ source "drivers/misc/mic/Kconfig"
|
|||
source "drivers/misc/genwqe/Kconfig"
|
||||
source "drivers/misc/echo/Kconfig"
|
||||
source "drivers/misc/cxl/Kconfig"
|
||||
source "drivers/misc/rm-otgcontrol/Kconfig"
|
||||
endmenu
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#
|
||||
# Makefile for misc devices that really don't fit anywhere else.
|
||||
#
|
||||
|
||||
obj-${CONFIG_RM_OTGCONTROL} += rm-otgcontrol/
|
||||
obj-$(CONFIG_IBM_ASM) += ibmasm/
|
||||
obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
|
||||
obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
config RM_OTGCONTROL
|
||||
tristate "reMarkable OTG control"
|
||||
help
|
||||
Add reMarkable OTG control module, required to control access to/from external equipment
|
|
@ -0,0 +1,7 @@
|
|||
obj-${CONFIG_RM_OTGCONTROL} += otgcontrol.o
|
||||
otgcontrol-objs := otgcontrol_main.o
|
||||
otgcontrol-objs += otgcontrol_sysfs.o
|
||||
otgcontrol-objs += otgcontrol_fsm.o
|
||||
otgcontrol-objs += otgcontrol_onewire.o
|
||||
otgcontrol-objs += otgcontrol_charging_ctrl.o
|
||||
otgcontrol-objs += otgcontrol_dr_mode.o
|
|
@ -0,0 +1,46 @@
|
|||
#include "otgcontrol_charging_ctrl.h"
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
int otgcontrol_change_otg_charge_mode(struct rm_otgcontrol_data *otgc_data, int mode)
|
||||
{
|
||||
union power_supply_propval property_val;
|
||||
|
||||
printk("%s: Enter\n", __func__);
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case OTG1_CHARGERMODE_CHARGE:
|
||||
printk("%s: Setting OTG1 chargermode (CHARGE)\n", __func__);
|
||||
property_val.intval = POWER_SUPPLY_MODE_CHARGER;
|
||||
power_supply_set_property(otgc_data->pdata->vbus_supply,
|
||||
POWER_SUPPLY_PROP_CHARGER_MODE,
|
||||
&property_val);
|
||||
property_val.intval = 1;
|
||||
power_supply_set_property(otgc_data->pdata->vbus_supply,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
&property_val);
|
||||
break;
|
||||
|
||||
case OTG1_CHARGERMODE_OTG:
|
||||
printk("%s: Setting OTG1 chargermode (OTG)\n", __func__);
|
||||
property_val.intval = POWER_SUPPLY_MODE_OTG_SUPPLY;
|
||||
power_supply_set_property(otgc_data->pdata->vbus_supply,
|
||||
POWER_SUPPLY_PROP_CHARGER_MODE,
|
||||
&property_val);
|
||||
property_val.intval = 1;
|
||||
power_supply_set_property(otgc_data->pdata->vbus_supply,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
&property_val);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("%s: Unable to set OTG1 chargermode (invalid mode %d)", __func__, mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
otgc_data->otg1_chargermode = mode;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef __OTGCONTROL_CHARGING_CTRL_H__
|
||||
#define __OTGCONTROL_CHARGING_CTRL_H__
|
||||
|
||||
#include <linux/rm-otgcontrol.h>
|
||||
|
||||
#define OTG1_CHARGERMODE_OTG 0
|
||||
#define OTG1_CHARGERMODE_CHARGE 1
|
||||
|
||||
int otgcontrol_change_otg_charge_mode(struct rm_otgcontrol_data *otgc_data, int mode);
|
||||
|
||||
#endif /* __OTGCONTROL_CHARGING_CTRL_H__ */
|
|
@ -0,0 +1,28 @@
|
|||
#include "otgcontrol_dr_mode.h"
|
||||
|
||||
int otgcontrol_set_dr_mode(struct rm_otgcontrol_data *otgc_dta, int mode)
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
case OTG1_DR_MODE__DEVICE:
|
||||
printk("%s: Switching OTG1 DR mode -> DEVICE\n", __func__);
|
||||
/* Set ID pin emulation to LOW */
|
||||
break;
|
||||
|
||||
case OTG1_DR_MODE__HOST:
|
||||
printk("%s: Switching OTG1 DR mode -> HOST\n", __func__);
|
||||
/* Set ID pin emulation to HIGH */
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("%s: unable to switch OTG1 DR mode (unknown mode %d)\n", __func__, mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int otgcontrol_get_dr_mode(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
/* Just return the last stored value */
|
||||
return otgc_data->otg1_dr_mode;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __OTGCONTROL_DR_MODE_H__
|
||||
#define __OTGCONTROL_DR_MODE_H__
|
||||
|
||||
#include <linux/rm-otgcontrol.h>
|
||||
|
||||
#define OTG1_DR_MODE__DEVICE 0
|
||||
#define OTG1_DR_MODE__HOST 1
|
||||
|
||||
int otgcontrol_set_dr_mode(struct rm_otgcontrol_data *otgc_dta, int mode);
|
||||
int otgcontrol_get_dr_mode(struct rm_otgcontrol_data *otgc_data);
|
||||
|
||||
#endif // __OTGCONTROL_DR_MODE_H__
|
|
@ -0,0 +1,109 @@
|
|||
#include <linux/rm-otgcontrol.h>
|
||||
|
||||
#include "otgcontrol_fsm.h"
|
||||
#include "otgcontrol_onewire.h"
|
||||
#include "otgcontrol_charging_ctrl.h"
|
||||
|
||||
#include <linux/printk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
int otgcontrol_init_fsm(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
printk("%s: Enter\n", __func__);
|
||||
|
||||
printk("%s: Initiating onewire state to GPIO\n", __func__);
|
||||
otgcontrol_init_one_wire_mux_state(otgc_data);
|
||||
|
||||
printk("%s: Checking if device is connected\n", __func__);
|
||||
if(otgcontrol_get_current_gpio_state(otgc_data) == 0) {
|
||||
printk("%s: Device is connected, running onewire authentication process", __func__);
|
||||
otgcontrol_start_onewire_authentication(otgc_data);
|
||||
}
|
||||
else {
|
||||
printk("%s: Device is not connected\n", __func__);
|
||||
printk("%s: Setting OTG1 mode (CHARGE)", __func__);
|
||||
otgcontrol_change_otg_charge_mode(otgc_data, OTG1_CHARGERMODE_CHARGE);
|
||||
|
||||
printk("%s: Activating onewire gpio interrupt\n", __func__);
|
||||
otgcontrol_activate_gpio_irq(otgc_data);
|
||||
|
||||
printk("%s: Waiting for low on GPIO input when device is connected", __func__);
|
||||
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_NOT_CONNECTED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(otgcontrol_init_fsm);
|
||||
|
||||
int otgcontrol_set_controlmode(struct rm_otgcontrol_data *otgc_data, int mode)
|
||||
{
|
||||
printk("%s: Enter\n", __func__);
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case OTG_MODE__MANUAL_CONTROL:
|
||||
printk("%s: setting MANUAL_CONTROL mode\n", __func__);
|
||||
break;
|
||||
|
||||
case OTG_MODE__ONEWIRE_AUTH:
|
||||
printk("%s: setting ONEWIRE_AUTH mode\n", __func__);
|
||||
break;
|
||||
|
||||
case OTG_MODE__USB_NO_AUTH:
|
||||
printk("%s: setting USB_NO_AUTH mode\n", __func__);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("%s: unable to set control mode (unknown mode %d)\n", __func__, mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int otgcontrol_handleInput(struct rm_otgcontrol_data *otgc_data, int signal, void* param)
|
||||
{
|
||||
printk("%s: Enter\n", __func__);
|
||||
|
||||
switch(signal)
|
||||
{
|
||||
case OTG1_EVENT__CHALLENGE_REPLY_RECEIVED:
|
||||
printk("%s: CHALLENGE REPLY RECEIVED\n", __func__);
|
||||
break;
|
||||
|
||||
case OTG1_EVENT__MODE_CHANGE_REQUESTED:
|
||||
printk("%s: MODE CHANGE REQUESTED\n", __func__);
|
||||
break;
|
||||
|
||||
case OTG1_EVENT__ONEWIRE_GPIO_STATE_CHANGED:
|
||||
printk("%s: ONEWIRE GPIO STATE CHANGED\n", __func__);
|
||||
break;
|
||||
|
||||
case OTG1_EVENT__OTG_CHARGERMODE_CHANGE_REQUESTED:
|
||||
printk("%s: CHARGERMODE CHANGE REQUESTED\n", __func__);
|
||||
break;
|
||||
|
||||
case OTG1_EVENT__TIMEOUT:
|
||||
printk("%s: TIMEOUT\n", __func__);
|
||||
break;
|
||||
|
||||
case OTG1_EVENT__VBUS_CHANGED:
|
||||
printk("%s: VBUS CHANGED\n", __func__);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("%s: Unknown signal/event (%d)\n", __func__, signal);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int otgcontrol_start_onewire_authentication(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
printk("%s: Enter\n", __func__);
|
||||
|
||||
printk("%s: PLEASE IMPLEMENT THIS\n", __func__);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef __OTGCONTROL_FSM_H__
|
||||
#define __OTGCONTROL_FSM_H__
|
||||
|
||||
#include <linux/rm-otgcontrol.h>
|
||||
|
||||
int otgcontrol_init_fsm(struct rm_otgcontrol_data *otgc_data);
|
||||
int otgcontrol_set_controlmode(struct rm_otgcontrol_data *otgc_data, int mode);
|
||||
int otgcontrol_handleInput(struct rm_otgcontrol_data *otgc_data, int signal, void *param);
|
||||
|
||||
static int otgcontrol_start_onewire_authentication(struct rm_otgcontrol_data *otgc_data);
|
||||
|
||||
#define OTG_MODE__MANUAL_CONTROL 0
|
||||
#define OTG_MODE__ONEWIRE_AUTH 1
|
||||
#define OTG_MODE__USB_NO_AUTH 2
|
||||
|
||||
#define OTG1_STATE__MANUAL_CONTROL 0
|
||||
#define OTG1_STATE__ONEWIRE_NOT_CONNECTED 1
|
||||
#define OTG1_STATE__ONEWIRE_WAIT_HANDSHAKE_RESPONS 2
|
||||
#define OTG1_STATE__ONEWIRE_DEVICE_CONNECTED 3
|
||||
#define OTG1_STATE__USB_NOT_CONNECTED 4
|
||||
#define OTG1_STATE__USB_DEVICE_CONNECTED 5
|
||||
#define OTG1_STATE__HOST_CONNECTED 6
|
||||
|
||||
#define OTG1_EVENT__VBUS_CHANGED 0
|
||||
#define OTG1_EVENT__ONEWIRE_GPIO_STATE_CHANGED 1
|
||||
#define OTG1_EVENT__CHALLENGE_REPLY_RECEIVED 2
|
||||
#define OTG1_EVENT__TIMEOUT 3
|
||||
#define OTG1_EVENT__MODE_CHANGE_REQUESTED 4
|
||||
#define OTG1_EVENT__OTG_CHARGERMODE_CHANGE_REQUESTED 5
|
||||
#define OTG1_STATE__OTG_ID_STATE_CHANGE_REQUESTED 6
|
||||
|
||||
#endif /* __OTGCONTROL_FSM_H__ */
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Sample kobject implementation
|
||||
*
|
||||
* Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
|
||||
* Copyright (C) 2007 Novell Inc.
|
||||
*
|
||||
* Released under the GPL version 2 only.
|
||||
*
|
||||
*/
|
||||
/* Internal includes */
|
||||
#include <linux/rm-otgcontrol.h>
|
||||
|
||||
#include "otgcontrol_sysfs.h"
|
||||
#include "otgcontrol_fsm.h"
|
||||
|
||||
/* Required Linux includes */
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
/* Sysfs */
|
||||
#include <linux/kobject.h>
|
||||
|
||||
/* Device Tree */
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
/* Linking to charger/vbus supply driver */
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
/* Faking of VID signal to otg driver */
|
||||
#include <linux/extcon.h>
|
||||
|
||||
static int rm_otgcontrol_init(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
printk("%s: Initiating sysfs nodes\n", __func__);
|
||||
ret = otgcontrol_init_sysfs_nodes(otgc_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
printk("%s: Initiating default ONEWIRE_AUTH state\n", __func__);
|
||||
ret = otgcontrol_init_fsm(otgc_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int rm_otgcontrol_parse_dt(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
struct device *dev = otgc_data->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct rm_otgcontrol_platform_data *pdata = otgc_data->pdata;
|
||||
const char *vbus_supply_name;
|
||||
int ret = 0;
|
||||
|
||||
printk("[---- SBA ----] %s: Enter\n", __func__);
|
||||
|
||||
if (of_find_property(np, "vbus-supply-name", NULL)) {
|
||||
printk("[---- SBA ----] %s: Found vbus-supply-name property, trying to read it\n", __func__);
|
||||
ret = of_property_read_string(np, "vbus-supply-name", &vbus_supply_name);
|
||||
if (ret) {
|
||||
printk("[---- SBA ----] %s: Failed to read property vbus-supply-name (code %d)\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
printk("[---- SBA ----] %s: Read vbus-supply-name: %s, trying to get reference to it\n", __func__, vbus_supply_name);
|
||||
pdata->vbus_supply = power_supply_get_by_name(vbus_supply_name);
|
||||
if (IS_ERR(pdata->vbus_supply)) {
|
||||
dev_err(dev, "%s: Failed to get supply '%s'\n", __func__, vbus_supply_name);
|
||||
return PTR_ERR(pdata->vbus_supply);
|
||||
}
|
||||
|
||||
if (!pdata->vbus_supply) {
|
||||
printk("[---- SBA ----] %s: vbus supply not ready, defering probe\n", __func__);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
printk("[---- SBA ----] %s: Got pointer to vbus-supply\n", __func__);
|
||||
}
|
||||
else {
|
||||
printk("[---- SBA ----] %s: Failed to get pointer to vbus-supply - verify that the charger driver is loaded !\n", __func__);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else
|
||||
otgc_data->pdata = pdata;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int rm_otgcontrol_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rm_otgcontrol_data *otgc_data;
|
||||
struct rm_otgcontrol_platform_data *pdata;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
printk("[---- SBA ----] %s: Enter:\n", __func__);
|
||||
|
||||
pr_info("%s: rM OTGCONTROL Driver Loading\n", __func__);
|
||||
|
||||
printk("[---- SBA ----] %s: Allocating otgcontrol_data\n", __func__);
|
||||
otgc_data = devm_kzalloc(&pdev->dev, sizeof(struct rm_otgcontrol_data), GFP_KERNEL);
|
||||
if (!otgc_data) {
|
||||
printk("[---- SBA ----] %s: Failed to allocate otgc_data\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
printk("[---- SBA ----] %s: Allocating otgcontrol_data\n", __func__);
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(struct rm_otgcontrol_platform_data), GFP_KERNEL);
|
||||
if (unlikely(!pdata)) {
|
||||
pr_err("[---- SBA ----] %s: Failed to allocate pdata\n", __func__);
|
||||
pdata = ERR_PTR(-ENOMEM);
|
||||
ret = -ENOMEM;
|
||||
goto error_1;
|
||||
}
|
||||
|
||||
otgc_data->dev = &pdev->dev;
|
||||
otgc_data->pdata = pdata;
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
printk("[---- SBA ---] %s: Reading platform data from devicetree\n", __func__);
|
||||
ret = rm_otgcontrol_parse_dt(otgc_data);
|
||||
if (ret < 0) {
|
||||
pr_err("[---- SBA ----] %s: Failed to load platform data from devicetree\n", __func__);
|
||||
goto error_2;
|
||||
}
|
||||
#else
|
||||
printk("[---- SBA ----] %s: Driver does not support non-dt configuration\n", __func__);
|
||||
ret = -ENOTSUP;
|
||||
goto error_2;
|
||||
#endif
|
||||
|
||||
printk("[---- SBA ----] %s: Setting otgc_data reference in pdev, and initiating\n", __func__);
|
||||
ret = rm_otgcontrol_init(otgc_data);
|
||||
if(ret < 0)
|
||||
goto error_2;
|
||||
|
||||
platform_set_drvdata(pdev, otgc_data);
|
||||
return 0;
|
||||
|
||||
error_1:
|
||||
kfree(otgc_data);
|
||||
return ret;
|
||||
|
||||
error_2:
|
||||
kfree(pdata);
|
||||
kfree(otgc_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rm_otgcontrol_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rm_otgcontrol_data *otgc_data = platform_get_drvdata(pdev);
|
||||
|
||||
printk("[---- SBA ----]otgcontrol1 %s Enter:\n", __func__);
|
||||
|
||||
printk("%s: Un-initializing sysfs nodes\n", __func__);
|
||||
otgcontrol_uninit_sysfs_nodes(otgc_data);
|
||||
|
||||
printk("%s: Freeing otgc->pdata\n", __func__);
|
||||
kfree(otgc_data->pdata);
|
||||
|
||||
printk("%s: Freeing otgc\n", __func__);
|
||||
kfree(otgc_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined CONFIG_PM
|
||||
static int rm_otgcontrol_suspend(struct device *dev)
|
||||
{
|
||||
printk("[---- SBA ----] %s Enter:\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm_otgcontrol_resume(struct device *dev)
|
||||
{
|
||||
printk("[---- SBA ----] %s Enter:\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define rm_otgcontrol_suspend NULL
|
||||
#define rm_otgcontrol_resume NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id rm_otgcontrol_dt_ids[] = {
|
||||
{ .compatible = "rm-otgcontrol" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rm_otgcontrol_dt_ids);
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(rm_otgcontrol_pm_ops,
|
||||
rm_otgcontrol_suspend,
|
||||
rm_otgcontrol_resume);
|
||||
|
||||
static struct platform_driver rm_otgcontrol_driver = {
|
||||
.driver = {
|
||||
.name = "rM OTG Control",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &rm_otgcontrol_pm_ops,
|
||||
#endif
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = rm_otgcontrol_dt_ids,
|
||||
#endif
|
||||
},
|
||||
.probe = rm_otgcontrol_probe,
|
||||
.remove = rm_otgcontrol_remove,
|
||||
};
|
||||
|
||||
static int __init otgcontrol_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk("%s: Registering platform driver 'rm-otgcontrol'\n", __func__);
|
||||
ret = platform_driver_register(&rm_otgcontrol_driver);
|
||||
if (ret < 0)
|
||||
printk("%s: Failed to register platform driver 'rm-otgcontrol', code %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit otgcontrol_exit(void)
|
||||
{
|
||||
printk("%s: Unregistering platform driver 'rm-otgcontrol'\n", __func__);
|
||||
platform_driver_unregister(&rm_otgcontrol_driver);
|
||||
}
|
||||
|
||||
module_init(otgcontrol_init);
|
||||
module_exit(otgcontrol_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("reMarkable OTG control driver, to enable authentication of devices connecting through the USB OTG interface");
|
||||
MODULE_VERSION("1.0");
|
||||
MODULE_AUTHOR("Steinar Bakkemo <steinar.bakkemo@remarkable.no");
|
|
@ -0,0 +1,121 @@
|
|||
#include "otgcontrol_onewire.h"
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
int otgcontrol_init_one_wire_mux_state(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk("%s: Initiating one-wire pinctrl states\n", __func__);
|
||||
otgc_data->one_wire_pinctrl = devm_pinctrl_get(otgc_data->dev);
|
||||
|
||||
otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__GPIO] = pinctrl_lookup_state(otgc_data->one_wire_pinctrl, "one_wire_gpio");
|
||||
if (IS_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__GPIO])) {
|
||||
devm_pinctrl_put(otgc_data->one_wire_pinctrl);
|
||||
return PTR_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__GPIO]);
|
||||
}
|
||||
|
||||
otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_TX] = pinctrl_lookup_state(otgc_data->one_wire_pinctrl, "one_wire_uart6_tx");
|
||||
if (IS_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_TX])) {
|
||||
devm_pinctrl_put(otgc_data->one_wire_pinctrl);
|
||||
return PTR_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_TX]);
|
||||
}
|
||||
|
||||
otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_RX] = pinctrl_lookup_state(otgc_data->one_wire_pinctrl, "one_wire_uart6_rx");
|
||||
if (IS_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_RX])) {
|
||||
devm_pinctrl_put(otgc_data->one_wire_pinctrl);
|
||||
return PTR_ERR(otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_RX]);
|
||||
}
|
||||
|
||||
printk("%s: Setting default state (GPIO)\n", __func__);
|
||||
ret = pinctrl_select_state(otgc_data->one_wire_pinctrl, otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__GPIO]);
|
||||
if (ret < 0) {
|
||||
devm_pinctrl_put(otgc_data->one_wire_pinctrl);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void otgcontrol_uninit_onw_wire_mux_state(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
devm_pinctrl_put(otgc_data->one_wire_pinctrl);
|
||||
}
|
||||
|
||||
int otgcontrol_switch_one_wire_mux_state(struct rm_otgcontrol_data *otgc_data, int state)
|
||||
{
|
||||
int ret;
|
||||
printk("%s: Enter\n", __func__);
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case OTG1_ONEWIRE_STATE__GPIO:
|
||||
printk("%s: Switching onewire state -> GPIO\n", __func__);
|
||||
ret = pinctrl_select_state(otgc_data->one_wire_pinctrl, otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__GPIO]);
|
||||
if (ret < 0) {
|
||||
printk("%s: Failed to set pinctrl state\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case OTG1_ONEWIRE_STATE__UART_RX:
|
||||
printk("%s: Switching onewire state -> UART RX\n", __func__);
|
||||
ret = pinctrl_select_state(otgc_data->one_wire_pinctrl, otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_RX]);
|
||||
if (ret < 0) {
|
||||
printk("%s: Failed to set pinctrl state\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case OTG1_ONEWIRE_STATE__UART_TX:
|
||||
printk("%s: switching onewire state -> UART TX\n", __func__);
|
||||
ret = pinctrl_select_state(otgc_data->one_wire_pinctrl, otgc_data->one_wire_pinctrl_states[OTG1_ONEWIRE_STATE__UART_TX]);
|
||||
if (ret < 0) {
|
||||
printk("%s: Failed to set pinctrl state\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("%s: unable to switch onewire state (unknown state %d)\n", __func__, state);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int otgcontrol_get_current_gpio_state(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
printk("%s: Enter\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int otgcontrol_init_gpio_irq(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
printk("%s: Enter\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void otgcontrol_activate_gpio_irq(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
printk("%s: Enter\n", __func__);
|
||||
}
|
||||
|
||||
int otgcontrol_deactivate_gpio_irq(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
printk("%s: Enter\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int otgcontrol_gpio_irq(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
printk("%s: Enter\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void otgcontrol_gpio_irq_work(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
printk("%s: Enter\n", __func__);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef __OTGCONTROL_ONE_WIRE_H__
|
||||
#define __OTGCONTROL_ONE_WIRE_H__
|
||||
|
||||
#include <linux/rm-otgcontrol.h>
|
||||
|
||||
#define OTG1_ONEWIRE_STATE__GPIO 0
|
||||
#define OTG1_ONEWIRE_STATE__UART_TX 1
|
||||
#define OTG1_ONEWIRE_STATE__UART_RX 2
|
||||
|
||||
int otgcontrol_init_one_wire_mux_state(struct rm_otgcontrol_data *otgc_data);
|
||||
void otgcontrol_uninit_onw_wire_mux_state(struct rm_otgcontrol_data *otgc_data);
|
||||
|
||||
int otgcontrol_switch_one_wire_mux_state(struct rm_otgcontrol_data *otgc_data, int newState);
|
||||
int otgcontrol_get_current_gpio_state(struct rm_otgcontrol_data *otgc_data);
|
||||
int otgcontrol_init_gpio_irq(struct rm_otgcontrol_data *otgc_data);
|
||||
void otgcontrol_activate_gpio_irq(struct rm_otgcontrol_data *otgc_data);
|
||||
int otgcontrol_deactivate_gpio_irq(struct rm_otgcontrol_data *otgc_data);
|
||||
int otgcontrol_gpio_irq(struct rm_otgcontrol_data *otgc_data);
|
||||
void otgcontrol_gpio_irq_work(struct rm_otgcontrol_data *otgc_dataS);
|
||||
|
||||
#endif /* __OTGCONTROL_ONE_WIRE_H__ */
|
|
@ -0,0 +1,178 @@
|
|||
#include "otgcontrol_sysfs.h"
|
||||
#include "otgcontrol_fsm.h"
|
||||
#include "otgcontrol_dr_mode.h"
|
||||
#include "otgcontrol_charging_ctrl.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
#define to_otgcontrol_data(kobj_attr_ptr, kobj_attr_member) container_of(kobj_attr_ptr, struct rm_otgcontrol_data, kobj_attr_member);
|
||||
|
||||
#define SYSFS_PARENT_NODE NULL
|
||||
#define SYSFS_NODE_NAME "otgcontrol"
|
||||
|
||||
static ssize_t attribute_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int var;
|
||||
struct rm_otgcontrol_data *otgc_data;
|
||||
|
||||
printk("%s: Enter\n", __func__);
|
||||
|
||||
if (strcmp(attr->attr.name, "otg1_device_connected") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr, otg1_device_connected_attribute);
|
||||
printk("%s: Returning cur otg1_device_connected value (%d)\n", __func__, otgc_data->otg1_device_connected);
|
||||
var = otgc_data->otg1_device_connected;
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_dr_mode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr, otg1_dr_mode_attribute);
|
||||
printk("%s: Returning cur otg1_id_state value (%d)\n", __func__, otgc_data->otg1_dr_mode);
|
||||
var = otgc_data->otg1_dr_mode;
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_chargermode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr, otg1_chargermode_attribute);
|
||||
printk("%s: Returning cur otg1_chargermode value (%d)\n", __func__, otgc_data->otg1_chargermode);
|
||||
var = otgc_data->otg1_chargermode;
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_controllermode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr, otg1_controllermode_attribute);
|
||||
printk("%s: Returning cur otg1_controllermode value (%d)\n", __func__, otgc_data->otg1_controllermode);
|
||||
var = otgc_data->otg1_controllermode;
|
||||
}
|
||||
else {
|
||||
printk("%s: Invalid attribute name (%s)\n", __func__, attr->attr.name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", var);
|
||||
}
|
||||
|
||||
static ssize_t attribute_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rm_otgcontrol_data *otgc_data;
|
||||
int var, ret;
|
||||
|
||||
printk("%s: Enter\n", __func__);
|
||||
|
||||
ret = kstrtoint(buf, 10, &var);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (strcmp(attr->attr.name, "otg1_dr_mode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr, otg1_dr_mode_attribute);
|
||||
printk("%s: Setting new otg1 dr mode (%d)\n", __func__, var);
|
||||
ret = otgcontrol_set_dr_mode(otgc_data, var);
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_chargermode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr, otg1_chargermode_attribute);
|
||||
printk("%s: Setting new otg1 chargermode (%d)\n", __func__, var);
|
||||
ret = otgcontrol_change_otg_charge_mode(otgc_data, var);
|
||||
}
|
||||
else if (strcmp(attr->attr.name, "otg1_controllermode") == 0) {
|
||||
otgc_data = to_otgcontrol_data(attr, otg1_controllermode_attribute);
|
||||
printk("%s: Setting new otg1 controllermode (%d)\n", __func__, var);
|
||||
ret = otgcontrol_handleInput(otgc_data, OTG1_EVENT__MODE_CHANGE_REQUESTED, (void*)&var);
|
||||
}
|
||||
else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void otgcontrol_create_kobj_property(struct kobj_attribute *attr,
|
||||
const char *name,
|
||||
int permission,
|
||||
ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf),
|
||||
ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count))
|
||||
{
|
||||
attr->attr.name = name;
|
||||
attr->attr.mode = VERIFY_OCTAL_PERMISSIONS(S_IRUGO | S_IWUSR);
|
||||
attr->show = show;
|
||||
attr->store = store;
|
||||
}
|
||||
|
||||
int otgcontrol_init_sysfs_nodes(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
struct kobject *otgcontrol_kobj;
|
||||
int retval;
|
||||
|
||||
printk("%s: Enter\n", __func__);
|
||||
|
||||
printk("%s: Creating control properties (R/W)\n", __func__);
|
||||
otgcontrol_create_kobj_property(&otgc_data->otg1_dr_mode_attribute,
|
||||
"otg1_dr_mode",
|
||||
S_IRUGO | S_IWUSR,
|
||||
attribute_show,
|
||||
attribute_store);
|
||||
|
||||
otgcontrol_create_kobj_property(&otgc_data->otg1_chargermode_attribute,
|
||||
"otg1_chargermode",
|
||||
S_IRUGO | S_IWUSR,
|
||||
attribute_show,
|
||||
attribute_store);
|
||||
|
||||
otgcontrol_create_kobj_property(&otgc_data->otg1_controllermode_attribute,
|
||||
"otg1_controllermode",
|
||||
S_IRUGO | S_IWUSR,
|
||||
attribute_show,
|
||||
attribute_store);
|
||||
|
||||
struct attribute *control_attrs[] = {
|
||||
&otgc_data->otg1_dr_mode_attribute.attr,
|
||||
&otgc_data->otg1_chargermode_attribute.attr,
|
||||
&otgc_data->otg1_controllermode_attribute.attr,
|
||||
NULL, /* need to NULL terminate the list of attributes */
|
||||
};
|
||||
|
||||
printk("%s: Creating status properties (R)\n", __func__);
|
||||
otgcontrol_create_kobj_property(&otgc_data->otg1_device_connected_attribute,
|
||||
"otg1_device_connected",
|
||||
S_IRUGO,
|
||||
attribute_show,
|
||||
attribute_store);
|
||||
|
||||
struct attribute *status_attrs[] = {
|
||||
&otgc_data->otg1_device_connected_attribute.attr,
|
||||
NULL, /* need to NULL terminate the list of attributes */
|
||||
};
|
||||
|
||||
struct attribute_group control_attr_group = {
|
||||
.attrs = control_attrs,
|
||||
.name = "control"
|
||||
};
|
||||
|
||||
struct attribute_group status_attr_group = {
|
||||
.attrs = status_attrs,
|
||||
.name = "status"
|
||||
};
|
||||
|
||||
otgcontrol_kobj = kobject_create_and_add(SYSFS_NODE_NAME, SYSFS_PARENT_NODE);
|
||||
if (!otgcontrol_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create the files associated with this kobject */
|
||||
retval = sysfs_create_group(otgcontrol_kobj, &control_attr_group);
|
||||
retval = sysfs_create_group(otgcontrol_kobj, &status_attr_group);
|
||||
if (retval) {
|
||||
otgc_data->kobject = otgcontrol_kobj;
|
||||
}
|
||||
|
||||
kobject_put(otgc_data->kobject);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL(otgcontrol_init_sysfs_nodes);
|
||||
|
||||
void otgcontrol_uninit_sysfs_nodes(struct rm_otgcontrol_data *otgc_data)
|
||||
{
|
||||
printk("%s: Enter\n", __func__);
|
||||
printk("%s: Decrementing kobject refcount\n", __func__);
|
||||
if(!IS_ERR(otgc_data->kobject))
|
||||
kobject_put(otgc_data->kobject);
|
||||
}
|
||||
EXPORT_SYMBOL(otgcontrol_uninit_sysfs_nodes);
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef __OTGCONTROL_SYSFS_H__
|
||||
#define __OTGCONTROL_SYSFS_H__
|
||||
|
||||
#include <linux/rm-otgcontrol.h>
|
||||
|
||||
int otgcontrol_init_sysfs_nodes(struct rm_otgcontrol_data *otgc_data);
|
||||
void otgcontrol_uninit_sysfs_nodes(struct rm_otgcontrol_data *otgc_data);
|
||||
|
||||
#endif /* __OTGCONTROL_SYSFS_H__ */
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef __RM_OTGCONTROL_H_
|
||||
#define __RM_OTGCONTROL_H_
|
||||
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
|
||||
struct rm_otgcontrol_platform_data {
|
||||
struct power_supply *vbus_supply;
|
||||
};
|
||||
|
||||
struct rm_otgcontrol_data {
|
||||
struct device *dev;
|
||||
struct rm_otgcontrol_platform_data *pdata;
|
||||
|
||||
int otg_controlstate;
|
||||
|
||||
int one_wire_state;
|
||||
struct pinctrl* one_wire_pinctrl;
|
||||
struct pinctrl_state* one_wire_pinctrl_states[3];
|
||||
|
||||
struct kobject* kobject;
|
||||
struct kobj_attribute otg1_device_connected_attribute;
|
||||
bool otg1_device_connected;
|
||||
|
||||
struct kobj_attribute otg1_dr_mode_attribute;
|
||||
int otg1_dr_mode;
|
||||
|
||||
struct kobj_attribute otg1_chargermode_attribute;
|
||||
int otg1_chargermode;
|
||||
|
||||
struct kobj_attribute otg1_controllermode_attribute;
|
||||
int otg1_controllermode;
|
||||
};
|
||||
|
||||
#endif /* __RM_OTGCONTROL_H */
|
Loading…
Reference in New Issue